From 419a81034c644b5b70e17b9756ac77b09bd36ec4 Mon Sep 17 00:00:00 2001 From: "andrew.greene" Date: Wed, 8 Dec 2021 17:51:12 -0700 Subject: [PATCH 01/53] Add Notice of derivative work --- NOTICE | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 NOTICE diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..496b655fa --- /dev/null +++ b/NOTICE @@ -0,0 +1,2 @@ +8 December 2021 +This project and all of its contents are a derivative work of https://github.com/node-red/node-red. All modifications, additions, and subtractions must adhere to the original LICENSE. \ No newline at end of file From b1daa8932a2f097e643042afdfaea765b954666c Mon Sep 17 00:00:00 2001 From: "andrew.greene" Date: Wed, 8 Dec 2021 18:01:31 -0700 Subject: [PATCH 02/53] Remove known unused files --- .gitignore | 1 + SECURITY.md | 5 - .../nodes/core/common/20-inject.html | 711 ------ .../@node-red/nodes/core/common/20-inject.js | 177 -- .../@node-red/nodes/core/common/21-debug.html | 513 ---- .../@node-red/nodes/core/common/21-debug.js | 297 --- .../nodes/core/common/24-complete.html | 154 -- .../nodes/core/common/24-complete.js | 31 - .../@node-red/nodes/core/common/25-catch.html | 191 -- .../@node-red/nodes/core/common/25-catch.js | 32 - .../nodes/core/common/25-status.html | 177 -- .../@node-red/nodes/core/common/25-status.js | 31 - .../@node-red/nodes/core/common/60-link.html | 340 --- .../@node-red/nodes/core/common/60-link.js | 132 - .../nodes/core/common/98-unknown.html | 23 - .../@node-red/nodes/core/common/98-unknown.js | 23 - .../nodes/core/function/10-function.html | 609 ----- .../nodes/core/function/10-function.js | 507 ---- .../nodes/core/function/10-switch.html | 440 ---- .../nodes/core/function/10-switch.js | 521 ---- .../nodes/core/function/15-change.html | 367 --- .../nodes/core/function/15-change.js | 357 --- .../nodes/core/function/16-range.html | 70 - .../@node-red/nodes/core/function/16-range.js | 55 - .../nodes/core/function/80-template.html | 155 -- .../nodes/core/function/80-template.js | 200 -- .../nodes/core/function/89-delay.html | 283 --- .../@node-red/nodes/core/function/89-delay.js | 424 ---- .../nodes/core/function/89-trigger.html | 234 -- .../nodes/core/function/89-trigger.js | 298 --- .../nodes/core/function/90-exec.html | 113 - .../@node-red/nodes/core/function/90-exec.js | 200 -- .../@node-red/nodes/core/function/rbe.html | 106 - .../@node-red/nodes/core/function/rbe.js | 97 - .../@node-red/nodes/core/network/05-tls.html | 197 -- .../@node-red/nodes/core/network/05-tls.js | 118 - .../nodes/core/network/06-httpproxy.html | 129 - .../nodes/core/network/06-httpproxy.js | 33 - .../@node-red/nodes/core/network/10-mqtt.html | 908 ------- .../@node-red/nodes/core/network/10-mqtt.js | 1139 --------- .../nodes/core/network/21-httpin.html | 273 -- .../@node-red/nodes/core/network/21-httpin.js | 356 --- .../nodes/core/network/21-httprequest.html | 249 -- .../nodes/core/network/21-httprequest.js | 673 ----- .../nodes/core/network/22-websocket.html | 288 --- .../nodes/core/network/22-websocket.js | 415 --- .../nodes/core/network/31-tcpin.html | 277 -- .../@node-red/nodes/core/network/31-tcpin.js | 719 ------ .../@node-red/nodes/core/network/32-udp.html | 231 -- .../@node-red/nodes/core/network/32-udp.js | 280 --- .../@node-red/nodes/core/network/lib/mqtt.js | 257 -- .../@node-red/nodes/core/parsers/70-CSV.html | 125 - .../@node-red/nodes/core/parsers/70-CSV.js | 319 --- .../@node-red/nodes/core/parsers/70-HTML.html | 64 - .../@node-red/nodes/core/parsers/70-HTML.js | 90 - .../@node-red/nodes/core/parsers/70-JSON.html | 62 - .../@node-red/nodes/core/parsers/70-JSON.js | 134 - .../@node-red/nodes/core/parsers/70-XML.html | 49 - .../@node-red/nodes/core/parsers/70-XML.js | 49 - .../@node-red/nodes/core/parsers/70-YAML.html | 37 - .../@node-red/nodes/core/parsers/70-YAML.js | 41 - .../nodes/core/sequence/17-split.html | 325 --- .../@node-red/nodes/core/sequence/17-split.js | 785 ------ .../nodes/core/sequence/18-sort.html | 119 - .../@node-red/nodes/core/sequence/18-sort.js | 266 -- .../nodes/core/sequence/19-batch.html | 170 -- .../@node-red/nodes/core/sequence/19-batch.js | 311 --- .../@node-red/nodes/core/storage/10-file.html | 347 --- .../@node-red/nodes/core/storage/10-file.js | 401 --- .../nodes/core/storage/23-watch.html | 53 - .../@node-red/nodes/core/storage/23-watch.js | 95 - .../debug/01 - Output payload value.json | 1 - .../debug/02 - Output complete message.json | 1 - .../common/debug/03 - Output to console.json | 1 - .../debug/04 - Output to node status.json | 1 - .../05 - Formatting output using JSONata.json | 1 - .../common/link/01 - Link within a tab.json | 1 - .../change/02 - Set any property value.json | 1 - .../change/03 - Set value using JSONata.json | 1 - .../change/04 - Set value from env var.json | 1 - .../change/05 - Set flow context.json | 1 - .../function/delay/01 - Delay message.json | 1 - ...2 - Delay message by message property.json | 1 - .../03 - Reset or flush pending message.json | 1 - ...5 - Limit message rate for each topic.json | 1 - ...standard output from external command.json | 1 - ...et error output from external command.json | 1 - ...02 - Scale input and round to integer.json | 1 - .../function/range/03 - Limit input.json | 1 - .../range/04 - Scale and wrap input.json | 1 - .../function/switch/02 - Check all rules.json | 1 - .../switch/03 - Stop after first match.json | 1 - .../04 - Select output port by type.json | 1 - .../05 - Use JSONata for switch rule.json | 1 - .../06 - Use JSONata for switch value.json | 1 - .../template/01 - Use mustache syntax.json | 1 - .../template/02 - Parse result as JSON.json | 1 - .../template/03 - Parse result as YAML.json | 1 - .../01 - Outputs two value with interval.json | 1 - .../http/03 - Handle URL parameters.json | 135 - .../http/06 - Post file to a flow.json | 126 - .../tcp/01 - Connect to TCP out server.json | 92 - .../tcp/02 - Connect to TCP in server.json | 92 - ...end reply to client of TCP connection.json | 174 -- ...01 - Transfer data using UDP protocol.json | 92 - .../01 - Connect to websocket in server.json | 96 - .../02 - Connect to websocket out server.json | 96 - ...fault column name as message sequence.json | 99 - ...CSV with default column name as array.json | 99 - ...ified column name as message sequence.json | 99 - ...name in first row as message sequence.json | 99 - ...05 - Convert JavaScript object to CSV.json | 99 - ...06 - Convert JavaScript object to CSV.json | 99 - ...bjects to CSV with column name header.json | 99 - ...Specify column names in input message.json | 126 - ...d column name when reset property set.json | 200 -- ...array of HTML element by CSS selector.json | 94 - ...uence of HTML element by CSS selector.json | 94 - ... by CSS selector specified in message.json | 121 - ...vert JSON string to JavaScript object.json | 92 - ...vert JavaScript object to JSON string.json | 92 - .../json/03 - Validate input JSON string.json | 160 -- ...01 - Convert JavaScript object to XML.json | 92 - ...02 - Convert XML to JavaScript object.json | 92 - ...rol conversion using options property.json | 119 - ...1 - Convert JavaScript object to YAML.json | 90 - ...2 - Convert YAML to JavaScript object.json | 90 - .../sort/01 - Sort array payload.json | 1 - .../sort/02 - Sort message sequence.json | 1 - .../split/01 - Split message payload.json | 1 - .../file-in/01 - Read string from a file.json | 113 - .../02 - Read data in specified encoding.json | 113 - ...ead data breaking lines into messages.json | 132 - .../file/01 - Write string to a file.json | 113 - ...tring to a file specified by property.json | 118 - .../storage/file/03 - Delete a file.json | 85 - ...04 - Specify encoding of written data.json | 113 - .../watch/01 - Watch change of a file.json | 108 - test/editor/editor_helper.js | 150 -- .../pageobjects/editor/debugTab_page.js | 40 - .../editor/pageobjects/editor/palette_page.js | 62 - .../pageobjects/editor/workspace_page.js | 101 - .../nodes/core/common/20-inject_page.js | 80 - .../nodes/core/common/21-debug_page.js | 43 - .../nodes/core/common/24-complete_page.js | 47 - .../nodes/core/common/25-catch_page.js | 48 - .../nodes/core/common/25-status_page.js | 48 - .../nodes/core/common/90-comment_page.js | 27 - .../nodes/core/function/10-function_page.js | 41 - .../nodes/core/function/10-switch_page.js | 234 -- .../nodes/core/function/15-change_page.js | 132 - .../nodes/core/function/16-range_page.js | 38 - .../nodes/core/function/80-template_page.js | 48 - .../nodes/core/function/89-delay_page.js | 31 - .../nodes/core/function/89-trigger_page.js | 83 - .../nodes/core/function/90-exec_page.js | 37 - .../nodes/core/network/10-mqtt_page.js | 74 - .../nodes/core/network/21-httpin_page.js | 35 - .../nodes/core/network/21-httprequest_page.js | 39 - .../core/network/21-httpresponse_page.js | 27 - .../nodes/core/network/22-websocket_page.js | 93 - .../nodes/core/parsers/70-CSV_page.js | 51 - .../nodes/core/parsers/70-HTML_page.js | 31 - .../nodes/core/parsers/70-JSON_page.js | 35 - .../nodes/core/parsers/70-XML_page.js | 35 - .../nodes/core/parsers/70-YAML_page.js | 35 - .../nodes/core/sequence/17-split_page.js | 47 - .../nodes/core/sequence/19-batch_page.js | 39 - .../nodes/core/storage/10-filein_page.js | 44 - test/editor/pageobjects/nodes/node_page.js | 51 - .../pageobjects/nodes/nodefactory_page.js | 94 - test/editor/pageobjects/util/key_page.js | 55 - .../editor/pageobjects/util/spec_util_page.js | 23 - test/editor/pageobjects/util/util_page.js | 84 - test/editor/specs/editor/workspace_uispec.js | 55 - .../scenario/cookbook_dataformats_uispec.js | 364 --- .../scenario/cookbook_errorhandling_uispec.js | 74 - .../scenario/cookbook_flowcontrol_uispec.js | 81 - .../scenario/cookbook_httpendpoints_uispec.js | 572 ----- .../scenario/cookbook_httprequests_uispec.js | 300 --- .../scenario/cookbook_messages_uispec.js | 142 -- .../specs/scenario/cookbook_mqtt_uispec.js | 224 -- test/editor/wdio.conf.js | 338 --- test/node_modules/nr-test-utils/index.js | 31 - test/node_modules/nr-test-utils/package.json | 6 - test/nodes/core/common/20-inject_spec.js | 669 ----- test/nodes/core/common/21-debug_spec.js | 659 ----- test/nodes/core/common/25-catch_spec.js | 44 - test/nodes/core/common/25-status_spec.js | 54 - test/nodes/core/common/60-link_spec.js | 187 -- test/nodes/core/common/90-comment_spec.js | 36 - test/nodes/core/common/98-unknown_spec.js | 36 - test/nodes/core/function/10-function_spec.js | 1721 ------------- test/nodes/core/function/10-switch_spec.js | 1153 --------- test/nodes/core/function/15-change_spec.js | 1911 -------------- test/nodes/core/function/16-range_spec.js | 152 -- test/nodes/core/function/80-template_spec.js | 498 ---- test/nodes/core/function/89-delay_spec.js | 1004 -------- test/nodes/core/function/89-trigger_spec.js | 1198 --------- test/nodes/core/function/90-exec_spec.js | 973 -------- test/nodes/core/function/rbe_spec.js | 538 ---- .../nodes/core/network/21-httprequest_spec.js | 2221 ----------------- test/nodes/core/network/22-websocket_spec.js | 568 ----- test/nodes/core/network/31-tcpin_spec.js | 224 -- test/nodes/core/network/31-tcprequest_spec.js | 301 --- test/nodes/core/network/32-udpin_spec.js | 94 - test/nodes/core/network/32-udpout_spec.js | 88 - test/nodes/core/parsers/70-CSV_spec.js | 1062 -------- test/nodes/core/parsers/70-HTML_spec.js | 494 ---- test/nodes/core/parsers/70-JSON_spec.js | 546 ---- test/nodes/core/parsers/70-XML_spec.js | 198 -- test/nodes/core/parsers/70-YAML_spec.js | 195 -- test/nodes/core/sequence/17-split_spec.js | 1919 -------------- test/nodes/core/sequence/18-sort_spec.js | 554 ---- test/nodes/core/sequence/19-batch_spec.js | 542 ---- test/nodes/core/storage/10-file_spec.js | 1722 ------------- test/nodes/core/storage/23-watch_spec.js | 217 -- test/nodes/subflow/subflow_spec.js | 570 ----- test/resources/70-HTML-test-file.html | 29 - test/resources/file-in-node/test.txt | 1 - test/resources/icons/test_icon.png | Bin 163 -> 0 bytes .../plugin/test-plugin/library-filestore.html | 22 - .../plugin/test-plugin/library-filestore.js | 37 - .../locales/en-US/library-filestore.json | 8 - .../locales/en-US/test-editor-plugin.json | 3 - .../resources/plugin/test-plugin/package.json | 13 - .../test-plugin/test-editor-plugin.html | 9 - .../plugin/test-plugin/test-runtime-plugin.js | 10 - test/resources/plugin/test-plugin/test.html | 6 - test/resources/plugin/test-plugin/test.js | 13 - test/resources/ssl/server.crt | 20 - test/resources/ssl/server.key | 27 - test/resources/subflow/package/README.md | 3 - test/resources/subflow/package/package.json | 19 - test/resources/subflow/package/subflow.js | 4 - test/resources/subflow/package/subflow.json | 269 -- .../subflow/test-subflow-mod-1.0.2.tgz | Bin 1709 -> 0 bytes .../editor-api/lib/admin/context_spec.js | 234 -- .../editor-api/lib/admin/flow_spec.js | 248 -- .../editor-api/lib/admin/flows_spec.js | 211 -- .../editor-api/lib/admin/index_spec.js | 458 ---- .../editor-api/lib/admin/nodes_spec.js | 497 ---- .../editor-api/lib/admin/plugins_spec.js | 111 - .../editor-api/lib/admin/settings_spec.js | 93 - .../editor-api/lib/auth/clients_spec.js | 47 - .../editor-api/lib/auth/index_spec.js | 216 -- .../editor-api/lib/auth/permissions_spec.js | 59 - .../editor-api/lib/auth/strategies_spec.js | 327 --- .../editor-api/lib/auth/tokens_spec.js | 180 -- .../editor-api/lib/auth/users_spec.js | 276 -- .../editor-api/lib/editor/comms_spec.js | 618 ----- .../editor-api/lib/editor/credentials_spec.js | 94 - .../editor-api/lib/editor/index_spec.js | 132 - .../editor-api/lib/editor/library_spec.js | 263 -- .../editor-api/lib/editor/locales_spec.js | 165 -- .../editor-api/lib/editor/projects_spec.js | 21 - .../editor-api/lib/editor/settings_spec.js | 97 - .../editor-api/lib/editor/sshkeys_spec.js | 333 --- .../editor-api/lib/editor/theme_spec.js | 275 -- .../editor-api/lib/editor/ui_spec.js | 210 -- .../@node-red/editor-api/lib/index_spec.js | 182 -- .../@node-red/editor-api/lib/util_spec.js | 110 - .../@node-red/registry/lib/deprecated_spec.js | 30 - .../registry/lib/externalModules_spec.js | 371 --- .../unit/@node-red/registry/lib/index_spec.js | 127 - .../@node-red/registry/lib/installer_spec.js | 528 ---- .../@node-red/registry/lib/library_spec.js | 62 - .../@node-red/registry/lib/loader_spec.js | 720 ------ .../registry/lib/localfilesystem_spec.js | 276 -- .../@node-red/registry/lib/plugins_spec.js | 200 -- .../@node-red/registry/lib/registry_spec.js | 614 ----- .../registry/lib/resources/examples/one.json | 0 .../lib/resources/local/DuffNode/DuffNode.js | 5 - .../local/DuplicateTestNode/TestNode1.html | 3 - .../local/DuplicateTestNode/TestNode1.js | 5 - .../local/MultipleNodes1/MultipleNodes1.html | 6 - .../local/MultipleNodes1/MultipleNodes1.js | 7 - .../NestedNode/NestedNode.html | 4 - .../NestedNode/NestedNode.js | 5 - .../NestedNode/icons/arrow-in.png | Bin 393 -> 0 bytes .../NestedNode/lib/ShouldNotLoad.html | 4 - .../NestedNode/lib/ShouldNotLoad.js | 5 - .../node_modules/ShouldNotLoad.html | 4 - .../NestedNode/node_modules/ShouldNotLoad.js | 5 - .../NestedNode/test/ShouldNotLoad.html | 4 - .../NestedNode/test/ShouldNotLoad.js | 5 - .../resources/local/TestNode1/TestNode1.html | 5 - .../resources/local/TestNode1/TestNode1.js | 5 - .../resources/local/TestNode2/TestNode2.html | 4 - .../resources/local/TestNode2/TestNode2.js | 9 - .../resources/local/TestNode3/TestNode3.html | 3 - .../resources/local/TestNode3/TestNode3.js | 7 - .../resources/local/TestNode4/TestNode4.html | 3 - .../resources/local/TestNode4/TestNode4.js | 1 - .../node_modules/EmptyModule/file.txt | 1 - .../TestNodeModule/TestNodeModule.html | 5 - .../TestNodeModule/TestNodeModule.js | 5 - .../TestNodeModule/TestNodeModule2.html | 5 - .../TestNodeModule/TestNodeModule2.js | 4 - .../TestNodeModule/icons/arrow-in.png | Bin 393 -> 0 bytes .../TestNodeModule/icons/file.txt | 3 - .../node_modules/TestNodeModule/package.json | 11 - .../VersionMismatchModule/TestNodeModule.html | 5 - .../VersionMismatchModule/TestNodeModule.js | 5 - .../TestNodeModule2.html | 5 - .../VersionMismatchModule/TestNodeModule2.js | 4 - .../VersionMismatchModule/icons/file.txt | 3 - .../VersionMismatchModule/package.json | 12 - .../lib/resources/userDir/lib/icons/file.txt | 0 .../resources/userDir/lib/icons/test_icon.png | Bin 163 -> 0 bytes .../userDir/nodes/TestNode5/TestNode5.html | 5 - .../userDir/nodes/TestNode5/TestNode5.js | 1 - .../@node-red/registry/lib/subflow_spec.js | 3 - test/unit/@node-red/registry/lib/util_spec.js | 115 - .../@node-red/runtime/lib/api/comms_spec.js | 321 --- .../@node-red/runtime/lib/api/context_spec.js | 353 --- .../@node-red/runtime/lib/api/flows_spec.js | 430 ---- .../@node-red/runtime/lib/api/index_spec.js | 55 - .../@node-red/runtime/lib/api/library_spec.js | 167 -- .../@node-red/runtime/lib/api/nodes_spec.js | 1005 -------- .../@node-red/runtime/lib/api/plugins_spec.js | 68 - .../runtime/lib/api/projects_spec.js | 1170 --------- .../runtime/lib/api/settings_spec.js | 988 -------- .../@node-red/runtime/lib/flows/Flow_spec.js | 1327 ---------- .../runtime/lib/flows/Subflow_spec.js | 885 ------- .../@node-red/runtime/lib/flows/index_spec.js | 669 ----- .../@node-red/runtime/lib/flows/util_spec.js | 801 ------ test/unit/@node-red/runtime/lib/index_spec.js | 250 -- .../runtime/lib/library/examples_spec.js | 138 - .../runtime/lib/library/index_spec.js | 199 -- .../runtime/lib/library/local_spec.js | 93 - .../@node-red/runtime/lib/nodes/Node_spec.js | 809 ------ .../runtime/lib/nodes/context/index_spec.js | 1209 --------- .../lib/nodes/context/localfilesystem_spec.js | 883 ------- .../runtime/lib/nodes/context/memory_spec.js | 321 --- .../runtime/lib/nodes/credentials_spec.js | 474 ---- .../@node-red/runtime/lib/nodes/index_spec.js | 404 --- .../NestedNode/icons/arrow-in.png | Bin 393 -> 0 bytes .../TestNodeModule/icons/arrow-in.png | 3 - .../@node-red/runtime/lib/plugins_spec.js | 13 - .../@node-red/runtime/lib/settings_spec.js | 333 --- .../runtime/lib/storage/index_spec.js | 271 -- .../lib/storage/localfilesystem/index_spec.js | 518 ---- .../storage/localfilesystem/library_spec.js | 244 -- .../localfilesystem/projects/Project_spec.js | 21 - .../projects/defaultFileSet_spec.js | 64 - .../projects/git/authCache_spec.js | 84 - .../projects/git/authServer_spec.js | 83 - .../projects/git/authWriter_spec.js | 83 - .../projects/git/index_spec.js | 21 - .../localfilesystem/projects/index_spec.js | 21 - .../projects/ssh/index_spec.js | 433 ---- .../projects/ssh/keygen_spec.js | 110 - .../storage/localfilesystem/sessions_spec.js | 80 - .../storage/localfilesystem/settings_spec.js | 134 - .../lib/storage/localfilesystem/util_spec.js | 32 - test/unit/@node-red/util/index_spec.js | 21 - test/unit/@node-red/util/lib/events_spec.js | 25 - test/unit/@node-red/util/lib/exec_spec.js | 140 -- test/unit/@node-red/util/lib/hooks_spec.js | 338 --- test/unit/@node-red/util/lib/i18n_spec.js | 26 - test/unit/@node-red/util/lib/index_spec.js | 19 - test/unit/@node-red/util/lib/log_spec.js | 252 -- test/unit/@node-red/util/lib/util_spec.js | 1092 -------- test/unit/_spec.js | 87 - test/unit/node-red/lib/red_spec.js | 76 - test/unit/node-red/red_spec.js | 21 - 367 files changed, 1 insertion(+), 75791 deletions(-) delete mode 100644 SECURITY.md delete mode 100644 packages/node_modules/@node-red/nodes/core/common/20-inject.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/20-inject.js delete mode 100644 packages/node_modules/@node-red/nodes/core/common/21-debug.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/21-debug.js delete mode 100644 packages/node_modules/@node-red/nodes/core/common/24-complete.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/24-complete.js delete mode 100644 packages/node_modules/@node-red/nodes/core/common/25-catch.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/25-catch.js delete mode 100644 packages/node_modules/@node-red/nodes/core/common/25-status.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/25-status.js delete mode 100644 packages/node_modules/@node-red/nodes/core/common/60-link.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/60-link.js delete mode 100644 packages/node_modules/@node-red/nodes/core/common/98-unknown.html delete mode 100644 packages/node_modules/@node-red/nodes/core/common/98-unknown.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/10-function.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/10-function.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/10-switch.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/10-switch.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/15-change.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/15-change.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/16-range.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/16-range.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/80-template.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/80-template.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/89-delay.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/89-delay.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/89-trigger.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/89-trigger.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/90-exec.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/90-exec.js delete mode 100644 packages/node_modules/@node-red/nodes/core/function/rbe.html delete mode 100644 packages/node_modules/@node-red/nodes/core/function/rbe.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/05-tls.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/05-tls.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/10-mqtt.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/10-mqtt.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/21-httpin.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/21-httpin.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/21-httprequest.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/21-httprequest.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/22-websocket.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/22-websocket.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/31-tcpin.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/31-tcpin.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/32-udp.html delete mode 100644 packages/node_modules/@node-red/nodes/core/network/32-udp.js delete mode 100644 packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-XML.html delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-XML.js delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html delete mode 100644 packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js delete mode 100644 packages/node_modules/@node-red/nodes/core/sequence/17-split.html delete mode 100644 packages/node_modules/@node-red/nodes/core/sequence/17-split.js delete mode 100644 packages/node_modules/@node-red/nodes/core/sequence/18-sort.html delete mode 100644 packages/node_modules/@node-red/nodes/core/sequence/18-sort.js delete mode 100644 packages/node_modules/@node-red/nodes/core/sequence/19-batch.html delete mode 100644 packages/node_modules/@node-red/nodes/core/sequence/19-batch.js delete mode 100755 packages/node_modules/@node-red/nodes/core/storage/10-file.html delete mode 100644 packages/node_modules/@node-red/nodes/core/storage/10-file.js delete mode 100644 packages/node_modules/@node-red/nodes/core/storage/23-watch.html delete mode 100644 packages/node_modules/@node-red/nodes/core/storage/23-watch.js delete mode 100644 test/editor/editor_helper.js delete mode 100644 test/editor/pageobjects/editor/debugTab_page.js delete mode 100644 test/editor/pageobjects/editor/palette_page.js delete mode 100644 test/editor/pageobjects/editor/workspace_page.js delete mode 100644 test/editor/pageobjects/nodes/core/common/20-inject_page.js delete mode 100644 test/editor/pageobjects/nodes/core/common/21-debug_page.js delete mode 100644 test/editor/pageobjects/nodes/core/common/24-complete_page.js delete mode 100644 test/editor/pageobjects/nodes/core/common/25-catch_page.js delete mode 100644 test/editor/pageobjects/nodes/core/common/25-status_page.js delete mode 100644 test/editor/pageobjects/nodes/core/common/90-comment_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/10-function_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/10-switch_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/15-change_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/16-range_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/80-template_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/89-delay_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/89-trigger_page.js delete mode 100644 test/editor/pageobjects/nodes/core/function/90-exec_page.js delete mode 100644 test/editor/pageobjects/nodes/core/network/10-mqtt_page.js delete mode 100644 test/editor/pageobjects/nodes/core/network/21-httpin_page.js delete mode 100644 test/editor/pageobjects/nodes/core/network/21-httprequest_page.js delete mode 100644 test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js delete mode 100644 test/editor/pageobjects/nodes/core/network/22-websocket_page.js delete mode 100644 test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js delete mode 100644 test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js delete mode 100644 test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js delete mode 100644 test/editor/pageobjects/nodes/core/parsers/70-XML_page.js delete mode 100644 test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js delete mode 100644 test/editor/pageobjects/nodes/core/sequence/17-split_page.js delete mode 100644 test/editor/pageobjects/nodes/core/sequence/19-batch_page.js delete mode 100644 test/editor/pageobjects/nodes/core/storage/10-filein_page.js delete mode 100644 test/editor/pageobjects/nodes/node_page.js delete mode 100644 test/editor/pageobjects/nodes/nodefactory_page.js delete mode 100644 test/editor/pageobjects/util/key_page.js delete mode 100644 test/editor/pageobjects/util/spec_util_page.js delete mode 100644 test/editor/pageobjects/util/util_page.js delete mode 100644 test/editor/specs/editor/workspace_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_dataformats_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_errorhandling_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_flowcontrol_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_httpendpoints_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_httprequests_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_messages_uispec.js delete mode 100644 test/editor/specs/scenario/cookbook_mqtt_uispec.js delete mode 100644 test/editor/wdio.conf.js delete mode 100644 test/node_modules/nr-test-utils/index.js delete mode 100644 test/node_modules/nr-test-utils/package.json delete mode 100644 test/nodes/core/common/20-inject_spec.js delete mode 100644 test/nodes/core/common/21-debug_spec.js delete mode 100644 test/nodes/core/common/25-catch_spec.js delete mode 100644 test/nodes/core/common/25-status_spec.js delete mode 100644 test/nodes/core/common/60-link_spec.js delete mode 100644 test/nodes/core/common/90-comment_spec.js delete mode 100644 test/nodes/core/common/98-unknown_spec.js delete mode 100644 test/nodes/core/function/10-function_spec.js delete mode 100644 test/nodes/core/function/10-switch_spec.js delete mode 100644 test/nodes/core/function/15-change_spec.js delete mode 100644 test/nodes/core/function/16-range_spec.js delete mode 100644 test/nodes/core/function/80-template_spec.js delete mode 100644 test/nodes/core/function/89-delay_spec.js delete mode 100644 test/nodes/core/function/89-trigger_spec.js delete mode 100644 test/nodes/core/function/90-exec_spec.js delete mode 100644 test/nodes/core/function/rbe_spec.js delete mode 100644 test/nodes/core/network/21-httprequest_spec.js delete mode 100644 test/nodes/core/network/22-websocket_spec.js delete mode 100644 test/nodes/core/network/31-tcpin_spec.js delete mode 100644 test/nodes/core/network/31-tcprequest_spec.js delete mode 100644 test/nodes/core/network/32-udpin_spec.js delete mode 100644 test/nodes/core/network/32-udpout_spec.js delete mode 100644 test/nodes/core/parsers/70-CSV_spec.js delete mode 100644 test/nodes/core/parsers/70-HTML_spec.js delete mode 100644 test/nodes/core/parsers/70-JSON_spec.js delete mode 100644 test/nodes/core/parsers/70-XML_spec.js delete mode 100644 test/nodes/core/parsers/70-YAML_spec.js delete mode 100644 test/nodes/core/sequence/17-split_spec.js delete mode 100644 test/nodes/core/sequence/18-sort_spec.js delete mode 100644 test/nodes/core/sequence/19-batch_spec.js delete mode 100644 test/nodes/core/storage/10-file_spec.js delete mode 100644 test/nodes/core/storage/23-watch_spec.js delete mode 100644 test/nodes/subflow/subflow_spec.js delete mode 100644 test/resources/70-HTML-test-file.html delete mode 100644 test/resources/file-in-node/test.txt delete mode 100644 test/resources/icons/test_icon.png delete mode 100644 test/resources/plugin/test-plugin/library-filestore.html delete mode 100644 test/resources/plugin/test-plugin/library-filestore.js delete mode 100644 test/resources/plugin/test-plugin/locales/en-US/library-filestore.json delete mode 100644 test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json delete mode 100644 test/resources/plugin/test-plugin/package.json delete mode 100644 test/resources/plugin/test-plugin/test-editor-plugin.html delete mode 100644 test/resources/plugin/test-plugin/test-runtime-plugin.js delete mode 100644 test/resources/plugin/test-plugin/test.html delete mode 100644 test/resources/plugin/test-plugin/test.js delete mode 100644 test/resources/ssl/server.crt delete mode 100644 test/resources/ssl/server.key delete mode 100644 test/resources/subflow/package/README.md delete mode 100644 test/resources/subflow/package/package.json delete mode 100644 test/resources/subflow/package/subflow.js delete mode 100644 test/resources/subflow/package/subflow.json delete mode 100644 test/resources/subflow/test-subflow-mod-1.0.2.tgz delete mode 100644 test/unit/@node-red/editor-api/lib/admin/context_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/admin/flow_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/admin/flows_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/admin/index_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/admin/nodes_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/admin/plugins_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/admin/settings_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/auth/clients_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/auth/index_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/auth/permissions_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/auth/strategies_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/auth/tokens_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/auth/users_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/comms_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/credentials_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/index_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/library_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/locales_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/projects_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/settings_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/theme_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/editor/ui_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/index_spec.js delete mode 100644 test/unit/@node-red/editor-api/lib/util_spec.js delete mode 100644 test/unit/@node-red/registry/lib/deprecated_spec.js delete mode 100644 test/unit/@node-red/registry/lib/externalModules_spec.js delete mode 100644 test/unit/@node-red/registry/lib/index_spec.js delete mode 100644 test/unit/@node-red/registry/lib/installer_spec.js delete mode 100644 test/unit/@node-red/registry/lib/library_spec.js delete mode 100644 test/unit/@node-red/registry/lib/loader_spec.js delete mode 100644 test/unit/@node-red/registry/lib/localfilesystem_spec.js delete mode 100644 test/unit/@node-red/registry/lib/plugins_spec.js delete mode 100644 test/unit/@node-red/registry/lib/registry_spec.js delete mode 100644 test/unit/@node-red/registry/lib/resources/examples/one.json delete mode 100644 test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt delete mode 100644 test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json delete mode 100644 test/unit/@node-red/registry/lib/resources/userDir/lib/icons/file.txt delete mode 100644 test/unit/@node-red/registry/lib/resources/userDir/lib/icons/test_icon.png delete mode 100644 test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html delete mode 100644 test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js delete mode 100644 test/unit/@node-red/registry/lib/subflow_spec.js delete mode 100644 test/unit/@node-red/registry/lib/util_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/comms_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/context_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/flows_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/library_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/nodes_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/plugins_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/projects_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/api/settings_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/flows/Flow_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/flows/Subflow_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/flows/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/flows/util_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/library/examples_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/library/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/library/local_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/Node_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/context/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/credentials_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png delete mode 100644 test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png delete mode 100644 test/unit/@node-red/runtime/lib/plugins_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/settings_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js delete mode 100644 test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js delete mode 100644 test/unit/@node-red/util/index_spec.js delete mode 100644 test/unit/@node-red/util/lib/events_spec.js delete mode 100644 test/unit/@node-red/util/lib/exec_spec.js delete mode 100644 test/unit/@node-red/util/lib/hooks_spec.js delete mode 100644 test/unit/@node-red/util/lib/i18n_spec.js delete mode 100644 test/unit/@node-red/util/lib/index_spec.js delete mode 100644 test/unit/@node-red/util/lib/log_spec.js delete mode 100644 test/unit/@node-red/util/lib/util_spec.js delete mode 100644 test/unit/_spec.js delete mode 100644 test/unit/node-red/lib/red_spec.js delete mode 100644 test/unit/node-red/red_spec.js diff --git a/.gitignore b/.gitignore index d4c991688..d52f81d76 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ docs .vscode .nyc_output sync.ffs_db +.idea \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 816ac507b..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly. diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.html b/packages/node_modules/@node-red/nodes/core/common/20-inject.html deleted file mode 100644 index 2cbf274ce..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/20-inject.html +++ /dev/null @@ -1,711 +0,0 @@ - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.js b/packages/node_modules/@node-red/nodes/core/common/20-inject.js deleted file mode 100644 index 7e25b1a1d..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/20-inject.js +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - const {scheduleTask} = require("cronosjs"); - - function InjectNode(n) { - RED.nodes.createNode(this,n); - - /* Handle legacy */ - if(!Array.isArray(n.props)){ - n.props = []; - n.props.push({ - p:'payload', - v:n.payload, - vt:n.payloadType - }); - n.props.push({ - p:'topic', - v:n.topic, - vt:'str' - }); - } else { - for (var i=0,l=n.props.length; i 2147483) { - node.error(RED._("inject.errors.toolong", this)); - delete node.repeat; - } - - node.repeaterSetup = function () { - if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) { - this.repeat = this.repeat * 1000; - if (RED.settings.verbose) { - this.log(RED._("inject.repeat", this)); - } - this.interval_id = setInterval(function() { - node.emit("input", {}); - }, this.repeat); - } else if (this.crontab) { - if (RED.settings.verbose) { - this.log(RED._("inject.crontab", this)); - } - this.cronjob = scheduleTask(this.crontab,() => { node.emit("input", {})}); - } - }; - - if (this.once) { - this.onceTimeout = setTimeout( function() { - node.emit("input",{}); - node.repeaterSetup(); - }, this.onceDelay); - } else { - node.repeaterSetup(); - } - - this.on("input", function(msg, send, done) { - var errors = []; - var props = this.props; - if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) { - props = msg.__user_inject_props__; - } - delete msg.__user_inject_props__; - props.forEach(p => { - var property = p.p; - var value = p.v ? p.v : ''; - var valueType = p.vt ? p.vt : 'str'; - - if (!property) return; - - if (valueType === "jsonata") { - if (p.exp) { - try { - var val = RED.util.evaluateJSONataExpression(p.exp, msg); - RED.util.setMessageProperty(msg, property, val, true); - } - catch (err) { - errors.push(err.message); - } - } - return; - } - try { - RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true); - } catch (err) { - errors.push(err.toString()); - } - }); - - if (errors.length) { - done(errors.join('; ')); - } else { - send(msg); - done(); - } - }); - } - - RED.nodes.registerType("inject",InjectNode); - - InjectNode.prototype.close = function() { - if (this.onceTimeout) { - clearTimeout(this.onceTimeout); - } - if (this.interval_id != null) { - clearInterval(this.interval_id); - if (RED.settings.verbose) { this.log(RED._("inject.stopped")); } - } else if (this.cronjob != null) { - this.cronjob.stop(); - if (RED.settings.verbose) { this.log(RED._("inject.stopped")); } - delete this.cronjob; - } - }; - - RED.httpAdmin.post("/inject/:id", RED.auth.needsPermission("inject.write"), function(req,res) { - var node = RED.nodes.getNode(req.params.id); - if (node != null) { - try { - if (req.body && req.body.__user_inject_props__) { - node.receive(req.body); - } else { - node.receive(); - } - res.sendStatus(200); - } catch(err) { - res.sendStatus(500); - node.error(RED._("inject.failed",{error:err.toString()})); - } - } else { - res.sendStatus(404); - } - }); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.html b/packages/node_modules/@node-red/nodes/core/common/21-debug.html deleted file mode 100644 index 195423482..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.html +++ /dev/null @@ -1,513 +0,0 @@ - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js deleted file mode 100644 index 73d364e43..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ /dev/null @@ -1,297 +0,0 @@ -module.exports = function(RED) { - "use strict"; - var util = require("util"); - var events = require("events"); - const fs = require("fs-extra"); - const path = require("path"); - var debuglength = RED.settings.debugMaxLength || 1000; - var useColors = RED.settings.debugUseColors || false; - util.inspect.styles.boolean = "red"; - - function DebugNode(n) { - var hasEditExpression = (n.targetType === "jsonata"); - var editExpression = hasEditExpression ? n.complete : null; - RED.nodes.createNode(this,n); - this.name = n.name; - this.complete = hasEditExpression ? null : (n.complete||"payload").toString(); - if (this.complete === "false") { this.complete = "payload"; } - this.console = ""+(n.console || false); - this.tostatus = n.tostatus || false; - this.statusType = n.statusType || "auto"; - this.statusVal = n.statusVal || this.complete; - this.tosidebar = n.tosidebar; - if (this.tosidebar === undefined) { this.tosidebar = true; } - this.active = (n.active === null || typeof n.active === "undefined") || n.active; - if (this.tostatus) { - this.status({fill:"grey", shape:"ring"}); - this.oldState = "{}"; - } - - var hasStatExpression = (n.statusType === "jsonata"); - var statExpression = hasStatExpression ? n.statusVal : null; - - var node = this; - var preparedEditExpression = null; - var preparedStatExpression = null; - if (editExpression) { - try { - preparedEditExpression = RED.util.prepareJSONataExpression(editExpression, this); - } - catch (e) { - node.error(RED._("debug.invalid-exp", {error: editExpression})); - return; - } - } - if (statExpression) { - try { - preparedStatExpression = RED.util.prepareJSONataExpression(statExpression, this); - } - catch (e) { - node.error(RED._("debug.invalid-exp", {error: editExpression})); - return; - } - } - - function prepareValue(msg, done) { - // Either apply the jsonata expression or... - if (preparedEditExpression) { - RED.util.evaluateJSONataExpression(preparedEditExpression, msg, (err, value) => { - if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); } - else { done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); } - }); - } else { - // Extract the required message property - var property = "payload"; - var output = msg[property]; - if (node.complete !== "false" && typeof node.complete !== "undefined") { - property = node.complete; - try { output = RED.util.getMessageProperty(msg,node.complete); } - catch(err) { output = undefined; } - } - done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, property:property, msg:output}); - } - } - - function prepareStatus(msg, done) { - if (node.statusType === "auto") { - if (node.complete === "true") { - done(null,{msg:msg.payload}); - } - else { - prepareValue(msg,function(err,debugMsg) { - if (err) { node.error(err); return; } - done(null,{msg:debugMsg.msg}); - }); - } - } - else { - // Either apply the jsonata expression or... - if (preparedStatExpression) { - RED.util.evaluateJSONataExpression(preparedStatExpression, msg, (err, value) => { - if (err) { done(RED._("debug.invalid-exp", {error:editExpression})); } - else { done(null,{msg:value}); } - }); - } - else { - // Extract the required message property - var output; - try { output = RED.util.getMessageProperty(msg,node.statusVal); } - catch(err) { output = undefined; } - done(null,{msg:output}); - } - } - } - this.on("close", function() { - if (this.oldState) { - this.status({}); - } - }) - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("status") && msg.status.hasOwnProperty("source") && msg.status.source.hasOwnProperty("id") && (msg.status.source.id === node.id)) { - done(); - return; - } - if (node.tostatus === true) { - prepareStatus(msg, function(err,debugMsg) { - if (err) { node.error(err); return; } - var output = debugMsg.msg; - var st = (typeof output === 'string') ? output : util.inspect(output); - var fill = "grey"; - var shape = "dot"; - if (typeof output === 'object' && output.hasOwnProperty("fill") && output.hasOwnProperty("shape") && output.hasOwnProperty("text")) { - fill = output.fill; - shape = output.shape; - st = output.text; - } - if (node.statusType === "auto") { - if (msg.hasOwnProperty("error")) { - fill = "red"; - st = msg.error.message; - } - if (msg.hasOwnProperty("status")) { - fill = msg.status.fill || "grey"; - shape = msg.status.shape || "ring"; - st = msg.status.text || ""; - } - } - - if (st.length > 32) { st = st.substr(0,32) + "..."; } - var newStatus = {fill:fill, shape:shape, text:st}; - if (JSON.stringify(newStatus) !== node.oldState) { // only send if we have to - node.status(newStatus); - node.oldState = JSON.stringify(newStatus); - } - }); - } - - if (this.complete === "true") { - // debug complete msg object - if (this.console === "true") { - node.log("\n"+util.inspect(msg, {colors:useColors, depth:10})); - } - if (this.active && this.tosidebar) { - sendDebug({id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:msg}); - } - done(); - } - else { - prepareValue(msg,function(err,debugMsg) { - if (err) { - node.error(err); - return; - } - var output = debugMsg.msg; - if (node.console === "true") { - if (typeof output === "string") { - node.log((output.indexOf("\n") !== -1 ? "\n" : "") + output); - } else if (typeof output === "object") { - node.log("\n"+util.inspect(output, {colors:useColors, depth:10})); - } else { - node.log(util.inspect(output, {colors:useColors})); - } - } - if (node.active) { - if (node.tosidebar == true) { - sendDebug(debugMsg); - } - } - done(); - }); - } - }) - } - - RED.nodes.registerType("debug",DebugNode, { - settings: { - debugUseColors: { - value: false, - }, - debugMaxLength: { - value: 1000, - } - } - }); - - function sendDebug(msg) { - // don't put blank errors in sidebar (but do add to logs) - //if ((msg.msg === "") && (msg.hasOwnProperty("level")) && (msg.level === 20)) { return; } - msg = RED.util.encodeObject(msg,{maxLength:debuglength}); - RED.comms.publish("debug",msg); - } - - DebugNode.logHandler = new events.EventEmitter(); - DebugNode.logHandler.on("log",function(msg) { - if (msg.level === RED.log.WARN || msg.level === RED.log.ERROR) { - sendDebug(msg); - } - }); - RED.log.addHandler(DebugNode.logHandler); - - function setNodeState(node,state) { - if (state) { - node.active = true; - } else { - node.active = false; - } - } - - RED.httpAdmin.post("/debug/:state", RED.auth.needsPermission("debug.write"), function(req,res) { - var state = req.params.state; - if (state !== 'enable' && state !== 'disable') { - res.sendStatus(404); - return; - } - var nodes = req.body && req.body.nodes; - if (Array.isArray(nodes)) { - nodes.forEach(function(id) { - var node = RED.nodes.getNode(id); - if (node !== null && typeof node !== "undefined" ) { - setNodeState(node, state === "enable"); - } - }) - res.sendStatus(state === "enable" ? 200 : 201); - } else { - res.sendStatus(400); - } - }) - - RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) { - var state = req.params.state; - if (state !== 'enable' && state !== 'disable') { - res.sendStatus(404); - return; - } - var node = RED.nodes.getNode(req.params.id); - if (node !== null && typeof node !== "undefined" ) { - setNodeState(node,state === "enable"); - res.sendStatus(state === "enable" ? 200 : 201); - } else { - res.sendStatus(404); - } - }); - - let cachedDebugView; - RED.httpAdmin.get("/debug/view/view.html", function(req,res) { - if (!cachedDebugView) { - fs.readFile(path.join(__dirname,"lib","debug","view.html")).then(data => { - let customStyles = ""; - try { - let customStyleList = RED.settings.editorTheme.page._.css || []; - customStyleList.forEach(style => { - customStyles += `\n` - }) - } catch(err) {} - cachedDebugView = data.toString().replace("",customStyles) - res.set('Content-Type', 'text/html'); - res.send(cachedDebugView).end(); - }).catch(err => { - res.sendStatus(404); - }) - } else { - res.send(cachedDebugView).end(); - } - - }); - - // As debug/view/debug-utils.js is loaded via - diff --git a/packages/node_modules/@node-red/nodes/core/common/24-complete.js b/packages/node_modules/@node-red/nodes/core/common/24-complete.js deleted file mode 100644 index ea665a265..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/24-complete.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function CompleteNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.scope = n.scope; - this.on("input",function(msg, send, done) { - send(msg); - done(); - }); - } - - RED.nodes.registerType("complete",CompleteNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/25-catch.html b/packages/node_modules/@node-red/nodes/core/common/25-catch.html deleted file mode 100644 index 0b976ea78..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-catch.html +++ /dev/null @@ -1,191 +0,0 @@ - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/25-catch.js b/packages/node_modules/@node-red/nodes/core/common/25-catch.js deleted file mode 100644 index 5ed525c36..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-catch.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function CatchNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.scope = n.scope; - this.uncaught = n.uncaught; - this.on("input",function(msg, send, done) { - send(msg); - done(); - }); - } - - RED.nodes.registerType("catch",CatchNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/25-status.html b/packages/node_modules/@node-red/nodes/core/common/25-status.html deleted file mode 100644 index 47a3192e4..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-status.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/25-status.js b/packages/node_modules/@node-red/nodes/core/common/25-status.js deleted file mode 100644 index fc6ccbe29..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-status.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function StatusNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.scope = n.scope; - this.on("input", function(msg, send, done) { - send(msg); - done(); - }); - } - - RED.nodes.registerType("status",StatusNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.html b/packages/node_modules/@node-red/nodes/core/common/60-link.html deleted file mode 100644 index f3fabba59..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.js b/packages/node_modules/@node-red/nodes/core/common/60-link.js deleted file mode 100644 index 53404e446..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - const crypto = require("crypto"); - - function LinkInNode(n) { - RED.nodes.createNode(this,n); - var node = this; - var event = "node:"+n.id; - var handler = function(msg) { - msg._event = n.event; - node.receive(msg); - } - RED.events.on(event,handler); - this.on("input", function(msg, send, done) { - send(msg); - done(); - }); - this.on("close",function() { - RED.events.removeListener(event,handler); - }); - } - - RED.nodes.registerType("link in",LinkInNode); - - function LinkOutNode(n) { - RED.nodes.createNode(this,n); - var node = this; - var mode = n.mode || "link"; - - var event = "node:"+n.id; - this.on("input", function(msg, send, done) { - msg._event = event; - RED.events.emit(event,msg) - - if (mode === "return") { - if (Array.isArray(msg._linkSource) && msg._linkSource.length > 0) { - var messageEvent = msg._linkSource.pop(); - var returnNode = RED.nodes.getNode(messageEvent.node); - if (returnNode && returnNode.returnLinkMessage) { - returnNode.returnLinkMessage(messageEvent.id, msg); - } else { - node.warn(RED._("link.error.missingReturn")) - } - } else { - node.warn(RED._("link.error.missingReturn")) - } - done(); - } else if (mode === "link") { - send(msg); - done(); - } - }); - } - RED.nodes.registerType("link out",LinkOutNode); - - - function LinkCallNode(n) { - RED.nodes.createNode(this,n); - const node = this; - const target = n.links[0]; - const messageEvents = {}; - let timeout = parseFloat(n.timeout || "30")*1000; - if (isNaN(timeout)) { - timeout = 30000; - } - - this.on("input", function(msg, send, done) { - msg._linkSource = msg._linkSource || []; - const messageEvent = { - id: crypto.randomBytes(14).toString('hex'), - node: node.id, - } - messageEvents[messageEvent.id] = { - msg: RED.util.cloneMessage(msg), - send, - done, - ts: setTimeout(function() { - timeoutMessage(messageEvent.id) - }, timeout ) - }; - msg._linkSource.push(messageEvent); - var targetNode = RED.nodes.getNode(target); - if (targetNode) { - targetNode.receive(msg); - } - }); - - this.returnLinkMessage = function(eventId, msg) { - if (Array.isArray(msg._linkSource) && msg._linkSource.length === 0) { - delete msg._linkSource; - } - const messageEvent = messageEvents[eventId]; - if (messageEvent) { - clearTimeout(messageEvent.ts); - delete messageEvents[eventId]; - messageEvent.send(msg); - messageEvent.done(); - } else { - node.send(msg); - } - } - - function timeoutMessage(eventId) { - const messageEvent = messageEvents[eventId]; - if (messageEvent) { - delete messageEvents[eventId]; - node.error("timeout",messageEvent.msg); - } - } - - } - RED.nodes.registerType("link call",LinkCallNode); - - -} diff --git a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html b/packages/node_modules/@node-red/nodes/core/common/98-unknown.html deleted file mode 100644 index 52071c30f..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/98-unknown.js b/packages/node_modules/@node-red/nodes/core/common/98-unknown.js deleted file mode 100644 index 0ee463bb3..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/98-unknown.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - function UnknownNode(n) { - RED.nodes.createNode(this,n); - } - RED.nodes.registerType("unknown",UnknownNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html deleted file mode 100644 index 4175f4448..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ /dev/null @@ -1,609 +0,0 @@ - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js deleted file mode 100644 index 4ce966e9d..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ /dev/null @@ -1,507 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var util = require("util"); - var vm = require("vm"); - var acorn = require("acorn"); - var acornWalk = require("acorn-walk"); - - function sendResults(node,send,_msgid,msgs,cloneFirstMessage) { - if (msgs == null) { - return; - } else if (!util.isArray(msgs)) { - msgs = [msgs]; - } - var msgCount = 0; - for (var m=0; m0) { - send(msgs); - } - } - - function createVMOpt(node, kind) { - var opt = { - filename: 'Function node'+kind+':'+node.id+(node.name?' ['+node.name+']':''), // filename for stack traces - displayErrors: true - // Using the following options causes node 4/6 to not include the line number - // in the stack output. So don't use them. - // lineOffset: -11, // line number offset to be used for stack traces - // columnOffset: 0, // column number offset to be used for stack traces - }; - return opt; - } - - function updateErrorInfo(err) { - if (err.stack) { - var stack = err.stack.toString(); - var m = /^([^:]+):([^:]+):(\d+).*/.exec(stack); - if (m) { - var line = parseInt(m[3]) -1; - var kind = "body:"; - if (/setup/.exec(m[1])) { - kind = "setup:"; - } - if (/cleanup/.exec(m[1])) { - kind = "cleanup:"; - } - err.message += " ("+kind+"line "+line+")"; - } - } - } - - function FunctionNode(n) { - RED.nodes.createNode(this,n); - var node = this; - node.name = n.name; - node.func = n.func; - node.outputs = n.outputs; - node.ini = n.initialize ? n.initialize.trim() : ""; - node.fin = n.finalize ? n.finalize.trim() : ""; - node.libs = n.libs || []; - - if (RED.settings.functionExternalModules === false && node.libs.length > 0) { - throw new Error(RED._("function.error.externalModuleNotAllowed")); - } - - - - var functionText = "var results = null;"+ - "results = (async function(msg,__send__,__done__){ "+ - "var __msgid__ = msg._msgid;"+ - "var node = {"+ - "id:__node__.id,"+ - "name:__node__.name,"+ - "outputCount:__node__.outputCount,"+ - "log:__node__.log,"+ - "error:__node__.error,"+ - "warn:__node__.warn,"+ - "debug:__node__.debug,"+ - "trace:__node__.trace,"+ - "on:__node__.on,"+ - "status:__node__.status,"+ - "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+ - "done:__done__"+ - "};\n"+ - node.func+"\n"+ - "})(msg,__send__,__done__);"; - - var handleNodeDoneCall = true; - - // Check to see if the Function appears to call `node.done()`. If so, - // we will assume it is well written and does actually call node.done(). - // Otherwise, we will call node.done() after the function returns regardless. - if (/node\.done\s*\(\s*\)/.test(functionText)) { - // We have spotted the code contains `node.done`. It could be in a comment - // so need to do the extra work to parse the AST and examine it properly. - acornWalk.simple(acorn.parse(functionText,{ecmaVersion: "latest"} ), { - CallExpression(astNode) { - if (astNode.callee && astNode.callee.object) { - if (astNode.callee.object.name === "node" && astNode.callee.property.name === "done") { - handleNodeDoneCall = false; - } - } - } - }) - } - - var finScript = null; - var finOpt = null; - node.topic = n.topic; - node.outstandingTimers = []; - node.outstandingIntervals = []; - node.clearStatus = false; - - var sandbox = { - console:console, - util:util, - Buffer:Buffer, - Date: Date, - RED: { - util: RED.util - }, - __node__: { - id: node.id, - name: node.name, - outputCount: node.outputs, - log: function() { - node.log.apply(node, arguments); - }, - error: function() { - node.error.apply(node, arguments); - }, - warn: function() { - node.warn.apply(node, arguments); - }, - debug: function() { - node.debug.apply(node, arguments); - }, - trace: function() { - node.trace.apply(node, arguments); - }, - send: function(send, id, msgs, cloneMsg) { - sendResults(node, send, id, msgs, cloneMsg); - }, - on: function() { - if (arguments[0] === "input") { - throw new Error(RED._("function.error.inputListener")); - } - node.on.apply(node, arguments); - }, - status: function() { - node.clearStatus = true; - node.status.apply(node, arguments); - } - }, - context: { - set: function() { - node.context().set.apply(node,arguments); - }, - get: function() { - return node.context().get.apply(node,arguments); - }, - keys: function() { - return node.context().keys.apply(node,arguments); - }, - get global() { - return node.context().global; - }, - get flow() { - return node.context().flow; - } - }, - flow: { - set: function() { - node.context().flow.set.apply(node,arguments); - }, - get: function() { - return node.context().flow.get.apply(node,arguments); - }, - keys: function() { - return node.context().flow.keys.apply(node,arguments); - } - }, - global: { - set: function() { - node.context().global.set.apply(node,arguments); - }, - get: function() { - return node.context().global.get.apply(node,arguments); - }, - keys: function() { - return node.context().global.keys.apply(node,arguments); - } - }, - env: { - get: function(envVar) { - var flow = node._flow; - return flow.getSetting(envVar); - } - }, - setTimeout: function () { - var func = arguments[0]; - var timerId; - arguments[0] = function() { - sandbox.clearTimeout(timerId); - try { - func.apply(node,arguments); - } catch(err) { - node.error(err,{}); - } - }; - timerId = setTimeout.apply(node,arguments); - node.outstandingTimers.push(timerId); - return timerId; - }, - clearTimeout: function(id) { - clearTimeout(id); - var index = node.outstandingTimers.indexOf(id); - if (index > -1) { - node.outstandingTimers.splice(index,1); - } - }, - setInterval: function() { - var func = arguments[0]; - var timerId; - arguments[0] = function() { - try { - func.apply(node,arguments); - } catch(err) { - node.error(err,{}); - } - }; - timerId = setInterval.apply(node,arguments); - node.outstandingIntervals.push(timerId); - return timerId; - }, - clearInterval: function(id) { - clearInterval(id); - var index = node.outstandingIntervals.indexOf(id); - if (index > -1) { - node.outstandingIntervals.splice(index,1); - } - } - }; - if (util.hasOwnProperty('promisify')) { - sandbox.setTimeout[util.promisify.custom] = function(after, value) { - return new Promise(function(resolve, reject) { - sandbox.setTimeout(function(){ resolve(value); }, after); - }); - }; - sandbox.promisify = util.promisify; - } - const moduleLoadPromises = []; - - if (node.hasOwnProperty("libs")) { - let moduleErrors = false; - var modules = node.libs; - modules.forEach(module => { - var vname = module.hasOwnProperty("var") ? module.var : null; - if (vname && (vname !== "")) { - if (sandbox.hasOwnProperty(vname) || vname === 'node') { - node.error(RED._("function.error.moduleNameError",{name:vname})) - moduleErrors = true; - return; - } - sandbox[vname] = null; - var spec = module.module; - if (spec && (spec !== "")) { - moduleLoadPromises.push(RED.import(module.module).then(lib => { - sandbox[vname] = lib.default; - }).catch(err => { - node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()})) - throw err; - })); - } - } - }); - if (moduleErrors) { - throw new Error(RED._("function.error.externalModuleLoadError")); - } - } - const RESOLVING = 0; - const RESOLVED = 1; - const ERROR = 2; - var state = RESOLVING; - var messages = []; - var processMessage = (() => {}); - - node.on("input", function(msg,send,done) { - if(state === RESOLVING) { - messages.push({msg:msg, send:send, done:done}); - } - else if(state === RESOLVED) { - processMessage(msg, send, done); - } - }); - Promise.all(moduleLoadPromises).then(() => { - var context = vm.createContext(sandbox); - try { - var iniScript = null; - var iniOpt = null; - if (node.ini && (node.ini !== "")) { - var iniText = ` - (async function(__send__) { - var node = { - id:__node__.id, - name:__node__.name, - outputCount:__node__.outputCount, - log:__node__.log, - error:__node__.error, - warn:__node__.warn, - debug:__node__.debug, - trace:__node__.trace, - status:__node__.status, - send: function(msgs, cloneMsg) { - __node__.send(__send__, RED.util.generateId(), msgs, cloneMsg); - } - }; - `+ node.ini +` - })(__initSend__);`; - iniOpt = createVMOpt(node, " setup"); - iniScript = new vm.Script(iniText, iniOpt); - } - node.script = vm.createScript(functionText, createVMOpt(node, "")); - if (node.fin && (node.fin !== "")) { - var finText = `(function () { - var node = { - id:__node__.id, - name:__node__.name, - outputCount:__node__.outputCount, - log:__node__.log, - error:__node__.error, - warn:__node__.warn, - debug:__node__.debug, - trace:__node__.trace, - status:__node__.status, - send: function(msgs, cloneMsg) { - __node__.error("Cannot send from close function"); - } - }; - `+node.fin +` - })();`; - finOpt = createVMOpt(node, " cleanup"); - finScript = new vm.Script(finText, finOpt); - } - var promise = Promise.resolve(); - if (iniScript) { - context.__initSend__ = function(msgs) { node.send(msgs); }; - promise = iniScript.runInContext(context, iniOpt); - } - - processMessage = function (msg, send, done) { - var start = process.hrtime(); - context.msg = msg; - context.__send__ = send; - context.__done__ = done; - - node.script.runInContext(context); - context.results.then(function(results) { - sendResults(node,send,msg._msgid,results,false); - if (handleNodeDoneCall) { - done(); - } - - var duration = process.hrtime(start); - var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100; - node.metric("duration", msg, converted); - if (process.env.NODE_RED_FUNCTION_TIME) { - node.status({fill:"yellow",shape:"dot",text:""+converted}); - } - }).catch(err => { - if ((typeof err === "object") && err.hasOwnProperty("stack")) { - //remove unwanted part - var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/); - err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n'); - var stack = err.stack.split(/\r?\n/); - - //store the error in msg to be used in flows - msg.error = err; - - var line = 0; - var errorMessage; - if (stack.length > 0) { - while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) { - line++; - } - - if (line < stack.length) { - errorMessage = stack[line]; - var m = /:(\d+):(\d+)$/.exec(stack[line+1]); - if (m) { - var lineno = Number(m[1])-1; - var cha = m[2]; - errorMessage += " (line "+lineno+", col "+cha+")"; - } - } - } - if (!errorMessage) { - errorMessage = err.toString(); - } - done(errorMessage); - } - else if (typeof err === "string") { - done(err); - } - else { - done(JSON.stringify(err)); - } - }); - } - - node.on("close", function() { - if (finScript) { - try { - finScript.runInContext(context, finOpt); - } - catch (err) { - node.error(err); - } - } - while (node.outstandingTimers.length > 0) { - clearTimeout(node.outstandingTimers.pop()); - } - while (node.outstandingIntervals.length > 0) { - clearInterval(node.outstandingIntervals.pop()); - } - if (node.clearStatus) { - node.status({}); - } - }); - - promise.then(function (v) { - var msgs = messages; - messages = []; - while (msgs.length > 0) { - msgs.forEach(function (s) { - processMessage(s.msg, s.send, s.done); - }); - msgs = messages; - messages = []; - } - state = RESOLVED; - }).catch((error) => { - messages = []; - state = ERROR; - node.error(error); - }); - - } - catch(err) { - // eg SyntaxError - which v8 doesn't include line number information - // so we can't do better than this - updateErrorInfo(err); - node.error(err); - } - }).catch(err => { - node.error(RED._("function.error.externalModuleLoadError")); - }); - } - RED.nodes.registerType("function",FunctionNode, { - dynamicModuleList: "libs", - settings: { - functionExternalModules: { value: true, exportable: true } - } - }); - RED.library.register("functions"); -}; diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.html b/packages/node_modules/@node-red/nodes/core/function/10-switch.html deleted file mode 100644 index f6fc82f0a..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.html +++ /dev/null @@ -1,440 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.js b/packages/node_modules/@node-red/nodes/core/function/10-switch.js deleted file mode 100644 index aa1972221..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.js +++ /dev/null @@ -1,521 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var operators = { - 'eq': function(a, b) { return a == b; }, - 'neq': function(a, b) { return a != b; }, - 'lt': function(a, b) { return a < b; }, - 'lte': function(a, b) { return a <= b; }, - 'gt': function(a, b) { return a > b; }, - 'gte': function(a, b) { return a >= b; }, - 'btwn': function(a, b, c) { return (a >= b && a <= c) || (a <= b && a >= c); }, - 'cont': function(a, b) { return (a + "").indexOf(b) != -1; }, - 'regex': function(a, b, c, d) { return (a + "").match(new RegExp(b,d?'i':'')); }, - 'true': function(a) { return a === true; }, - 'false': function(a) { return a === false; }, - 'null': function(a) { return (typeof a == "undefined" || a === null); }, - 'nnull': function(a) { return (typeof a != "undefined" && a !== null); }, - 'empty': function(a) { - if (typeof a === 'string' || Array.isArray(a) || Buffer.isBuffer(a)) { - return a.length === 0; - } else if (typeof a === 'object' && a !== null) { - return Object.keys(a).length === 0; - } - return false; - }, - 'nempty': function(a) { - if (typeof a === 'string' || Array.isArray(a) || Buffer.isBuffer(a)) { - return a.length !== 0; - } else if (typeof a === 'object' && a !== null) { - return Object.keys(a).length !== 0; - } - return false; - }, - 'istype': function(a, b) { - if (b === "array") { return Array.isArray(a); } - else if (b === "buffer") { return Buffer.isBuffer(a); } - else if (b === "json") { - try { JSON.parse(a); return true; } // or maybe ??? a !== null; } - catch(e) { return false;} - } - else if (b === "null") { return a === null; } - else { return typeof a === b && !Array.isArray(a) && !Buffer.isBuffer(a) && a !== null; } - }, - 'head': function(a, b, c, d, parts) { - var count = Number(b); - return (parts.index < count); - }, - 'tail': function(a, b, c, d, parts) { - var count = Number(b); - return (parts.count -count <= parts.index); - }, - 'index': function(a, b, c, d, parts) { - var min = Number(b); - var max = Number(c); - var index = parts.index; - return ((min <= index) && (index <= max)); - }, - 'hask': function(a, b) { - return a !== undefined && a !== null && (typeof b !== "object" ) && a.hasOwnProperty(b+""); - }, - 'jsonata_exp': function(a, b) { return (b === true); }, - 'else': function(a) { return a === true; } - }; - - var _maxKeptCount; - - function getMaxKeptCount() { - if (_maxKeptCount === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _maxKeptCount = RED.settings[name]; - } - else { - _maxKeptCount = 0; - } - } - return _maxKeptCount; - } - - function getProperty(node,msg,done) { - if (node.propertyType === 'jsonata') { - RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => { - if (err) { - done(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - done(undefined,value); - } - }); - } else { - RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => { - if (err) { - done(undefined,undefined); - } else { - done(undefined,value); - } - }); - } - } - - function getV1(node,msg,rule,hasParts,done) { - if (rule.vt === 'prev') { - return done(undefined,node.previousValue); - } else if (rule.vt === 'jsonata') { - var exp = rule.v; - if (rule.t === 'jsonata_exp') { - if (hasParts) { - exp.assign("I", msg.parts.index); - exp.assign("N", msg.parts.count); - } - } - RED.util.evaluateJSONataExpression(exp,msg,(err,value) => { - if (err) { - done(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - done(undefined, value); - } - }); - } else if (rule.vt === 'json') { - done(undefined,"json"); // TODO: ?! invalid case - } else if (rule.vt === 'null') { - done(undefined,"null"); - } else { - RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) { - if (err) { - done(undefined, undefined); - } else { - done(undefined, value); - } - }); - } - } - - function getV2(node,msg,rule,done) { - var v2 = rule.v2; - if (rule.v2t === 'prev') { - return done(undefined,node.previousValue); - } else if (rule.v2t === 'jsonata') { - RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => { - if (err) { - done(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - done(undefined,value); - } - }); - } else if (typeof v2 !== 'undefined') { - RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) { - if (err) { - done(undefined,undefined); - } else { - done(undefined,value); - } - }); - } else { - done(undefined,v2); - } - } - - function applyRule(node, msg, property, state, done) { - var rule = node.rules[state.currentRule]; - var v1,v2; - - getV1(node,msg,rule,state.hasParts, (err,value) => { - if (err) { - // This only happens if v1 is an invalid JSONata expr - // But that will have already been logged and the node marked - // invalid as part of the constructor - return done(err); - } - v1 = value; - getV2(node,msg,rule, (err,value) => { - if (err) { - // This only happens if v1 is an invalid JSONata expr - // But that will have already been logged and the node marked - // invalid as part of the constructor - return done(err); - } - v2 = value; - if (rule.t == "else") { - property = state.elseflag; - state.elseflag = true; - } - try { - if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) { - state.onward.push(msg); - state.elseflag = false; - if (node.checkall == "false") { - return done(undefined,false); - } - } else { - state.onward.push(null); - } - done(undefined, state.currentRule < node.rules.length - 1); - } catch(err) { - // An error occurred evaluating the rule - for example, an - // invalid RegExp value. - done(err); - } - }); - }); - } - - function applyRules(node, msg, property,state,done) { - if (!state) { - if (node.rules.length === 0) { - done(undefined, []); - return; - } - state = { - currentRule: 0, - elseflag: true, - onward: [], - hasParts: msg.hasOwnProperty("parts") && - msg.parts.hasOwnProperty("id") && - msg.parts.hasOwnProperty("index") - } - } - applyRule(node,msg,property,state,(err,hasMore) => { - if (err) { - return done(err); - } - if (hasMore) { - state.currentRule++; - applyRules(node,msg,property,state,done); - } else { - node.previousValue = property; - done(undefined,state.onward); - } - }); - } - - - function SwitchNode(n) { - RED.nodes.createNode(this, n); - this.rules = n.rules || []; - this.property = n.property; - this.propertyType = n.propertyType || "msg"; - - if (this.propertyType === 'jsonata') { - try { - this.property = RED.util.prepareJSONataExpression(this.property,this); - } catch(err) { - this.error(RED._("switch.errors.invalid-expr",{error:err.message})); - return; - } - } - - this.checkall = n.checkall || "true"; - this.previousValue = null; - var node = this; - var valid = true; - var repair = n.repair; - var needsCount = repair; - - for (var i=0; i 0) && (pendingCount > max_msgs)) { - clearPending(); - node.error(RED._("switch.errors.too-many"), msg); - } - if (parts.hasOwnProperty("count")) { - group.count = parts.count; - } - return group; - } - - function drainMessageGroup(msgs,count,done) { - var msg = msgs.shift(); - msg.parts.count = count; - processMessage(msg,false, err => { - if (err) { - done(err); - } else { - if (msgs.length === 0) { - done() - } else { - drainMessageGroup(msgs,count,done); - } - } - }) - } - function addMessageToPending(msg,done) { - var parts = msg.parts; - // We've already checked the msg.parts has the require bits - var group = addMessageToGroup(parts.id, msg, parts); - var msgs = group.msgs; - var count = group.count; - var msgsCount = msgs.length; - if (count === msgsCount) { - // We have a complete group - send the individual parts - drainMessageGroup(msgs,count,err => { - pendingCount -= msgsCount; - delete pendingIn[parts.id]; - done(); - }) - return; - } - done(); - } - - function sendGroup(onwards, port_count) { - var counts = new Array(port_count).fill(0); - for (var i = 0; i < onwards.length; i++) { - var onward = onwards[i]; - for (var j = 0; j < port_count; j++) { - counts[j] += (onward[j] !== null) ? 1 : 0 - } - } - var ids = new Array(port_count); - for (var j = 0; j < port_count; j++) { - ids[j] = RED.util.generateId(); - } - var ports = new Array(port_count); - var indexes = new Array(port_count).fill(0); - for (var i = 0; i < onwards.length; i++) { - var onward = onwards[i]; - for (var j = 0; j < port_count; j++) { - var msg = onward[j]; - if (msg) { - var new_msg = RED.util.cloneMessage(msg); - var parts = new_msg.parts; - parts.id = ids[j]; - parts.index = indexes[j]; - parts.count = counts[j]; - ports[j] = new_msg; - indexes[j]++; - } - else { - ports[j] = null; - } - } - node.send(ports); - } - } - - function sendGroupMessages(onward, msg) { - var parts = msg.parts; - var gid = parts.id; - received[gid] = ((gid in received) ? received[gid] : 0) +1; - var send_ok = (received[gid] === parts.count); - - if (!(gid in pendingOut)) { - pendingOut[gid] = { - onwards: [] - }; - } - var group = pendingOut[gid]; - var onwards = group.onwards; - onwards.push(onward); - pendingCount++; - if (send_ok) { - sendGroup(onwards, onward.length, msg); - pendingCount -= onward.length; - delete pendingOut[gid]; - delete received[gid]; - } - var max_msgs = getMaxKeptCount(); - if ((max_msgs > 0) && (pendingCount > max_msgs)) { - clearPending(); - node.error(RED._("switch.errors.too-many"), msg); - } - } - - function processMessage(msg, checkParts, done) { - var hasParts = msg.hasOwnProperty("parts") && - msg.parts.hasOwnProperty("id") && - msg.parts.hasOwnProperty("index"); - - if (needsCount && checkParts && hasParts) { - addMessageToPending(msg,done); - } else { - getProperty(node,msg,(err,property) => { - if (err) { - node.warn(err); - done(); - } else { - applyRules(node,msg,property,undefined,(err,onward) => { - if (err) { - node.error(err, msg); - } else { - if (!repair || !hasParts) { - node.send(onward); - } else { - sendGroupMessages(onward, msg); - } - } - done(); - }); - } - }); - } - } - - function clearPending() { - pendingCount = 0; - pendingId = 0; - pendingIn = {}; - pendingOut = {}; - received = {}; - } - - var pendingMessages = []; - var handlingMessage = false; - var processMessageQueue = function(msg) { - if (msg) { - - // A new message has arrived - add it to the message queue - pendingMessages.push(msg); - if (handlingMessage) { - // The node is currently processing a message, so do nothing - // more with this message - return; - } - } - if (pendingMessages.length === 0) { - // There are no more messages to process, clear the active flag - // and return - handlingMessage = false; - return; - } - - // There are more messages to process. Get the next message and - // start processing it. Recurse back in to check for any more - var nextMsg = pendingMessages.shift(); - handlingMessage = true; - processMessage(nextMsg,true,err => { - if (err) { - node.error(err,nextMsg); - } - processMessageQueue() - }); - } - - this.on('input', function(msg) { - processMessageQueue(msg); - }); - - this.on('close', function() { - clearPending(); - }); - } - - RED.nodes.registerType("switch", SwitchNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.html b/packages/node_modules/@node-red/nodes/core/function/15-change.html deleted file mode 100644 index b40039028..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/15-change.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.js b/packages/node_modules/@node-red/nodes/core/function/15-change.js deleted file mode 100644 index d177caec8..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/15-change.js +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function ChangeNode(n) { - RED.nodes.createNode(this, n); - var node = this; - - this.rules = n.rules; - var rule; - if (!this.rules) { - rule = { - t:(n.action=="replace"?"set":n.action), - p:n.property||"" - } - - if ((rule.t === "set")||(rule.t === "move")) { - rule.to = n.to||""; - } else if (rule.t === "change") { - rule.from = n.from||""; - rule.to = n.to||""; - rule.re = (n.reg===null||n.reg); - } - this.rules = [rule]; - } - - var valid = true; - for (var i=0;i { - if (err) { - done(undefined,undefined); - } else { - done(undefined,value); - } - }); - return - } else if (rule.tot === 'date') { - value = Date.now(); - } else if (rule.tot === 'jsonata') { - RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => { - if (err) { - done(RED._("change.errors.invalid-expr",{error:err.message})) - } else { - done(undefined, value); - } - }); - return; - } - done(undefined,value); - } - - function getFromValueType(fromValue, done) { - var fromType; - var fromRE; - if (typeof fromValue === 'number' || fromValue instanceof Number) { - fromType = 'num'; - } else if (typeof fromValue === 'boolean') { - fromType = 'bool' - } else if (fromValue instanceof RegExp) { - fromType = 're'; - fromRE = fromValue; - } else if (typeof fromValue === 'string') { - fromType = 'str'; - fromRE = fromValue.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - try { - fromRE = new RegExp(fromRE, "g"); - } catch (e) { - done(new Error(RED._("change.errors.invalid-from",{error:e.message}))); - } - } else { - done(new Error(RED._("change.errors.invalid-from",{error:"unsupported type: "+(typeof fromValue)}))); - } - done(undefined,{ - fromType, - fromValue, - fromRE - }); - } - function getFromValue(msg,rule, done) { - var fromValue; - var fromType; - var fromRE; - if (rule.t === 'change') { - if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') { - if (rule.fromt === "msg") { - return getFromValueType(RED.util.getMessageProperty(msg,rule.from),done); - } else if (rule.fromt === 'flow' || rule.fromt === 'global') { - var contextKey = RED.util.parseContextStore(rule.from); - if (/\[msg\./.test(context.key)) { - // The key has a nest msg. reference to evaluate first - context.key = RED.util.normalisePropertyExpression(contextKey.key,msg,true); - } - node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => { - if (err) { - done(err) - } else { - getFromValueType(fromValue,done); - } - }); - return; - } - } else { - fromType = rule.fromt; - fromValue = rule.from; - fromRE = rule.fromRE; - } - } - done(undefined, { - fromType, - fromValue, - fromRE - }); - } - function applyRule(msg,rule,done) { - var property = rule.p; - var current; - var fromValue; - var fromType; - var fromRE; - - try { - getToValue(msg,rule,(err,value) => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } else { - if (rule.dc) { - value = RED.util.cloneMessage(value); - } - getFromValue(msg,rule,(err,fromParts) => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } else { - fromValue = fromParts.fromValue; - fromType = fromParts.fromType; - fromRE = fromParts.fromRE; - if (rule.pt === 'msg') { - try { - if (rule.t === 'delete') { - RED.util.setMessageProperty(msg,property,undefined); - } else if (rule.t === 'set') { - if (!RED.util.setMessageProperty(msg,property,value)) { - node.warn(RED._("change.errors.no-override",{property:property})); - } - } else if (rule.t === 'change') { - current = RED.util.getMessageProperty(msg,property); - if (typeof current === 'string') { - if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) { - // str representation of exact from number/boolean - // only replace if they match exactly - RED.util.setMessageProperty(msg,property,value); - } else { - current = current.replace(fromRE,value); - RED.util.setMessageProperty(msg,property,current); - } - } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') { - if (current == Number(fromValue)) { - RED.util.setMessageProperty(msg,property,value); - } - } else if (typeof current === 'boolean' && fromType === 'bool') { - if (current.toString() === fromValue) { - RED.util.setMessageProperty(msg,property,value); - } - } - } - } catch(err) {} - return done(undefined,msg); - } else if (rule.pt === 'flow' || rule.pt === 'global') { - var contextKey = RED.util.parseContextStore(property); - if (/\[msg/.test(contextKey.key)) { - // The key has a nest msg. reference to evaluate first - contextKey.key = RED.util.normalisePropertyExpression(contextKey.key, msg, true) - } - var target = node.context()[rule.pt]; - var callback = err => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } else { - done(undefined,msg); - } - } - if (rule.t === 'delete') { - target.set(contextKey.key,undefined,contextKey.store,callback); - } else if (rule.t === 'set') { - target.set(contextKey.key,value,contextKey.store,callback); - } else if (rule.t === 'change') { - target.get(contextKey.key,contextKey.store,(err,current) => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } - if (typeof current === 'string') { - if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) { - // str representation of exact from number/boolean - // only replace if they match exactly - target.set(contextKey.key,value,contextKey.store,callback); - } else { - current = current.replace(fromRE,value); - target.set(contextKey.key,current,contextKey.store,callback); - } - } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') { - if (current == Number(fromValue)) { - target.set(contextKey.key,value,contextKey.store,callback); - } - } else if (typeof current === 'boolean' && fromType === 'bool') { - if (current.toString() === fromValue) { - target.set(contextKey.key,value,contextKey.store,callback); - } - } - }); - } - } - } - }) - } - }); - } catch(err) { - // This is an okay error - done(undefined,msg); - } - } - function completeApplyingRules(msg,currentRule,done) { - if (!msg) { - return done(); - } else if (currentRule === node.rules.length - 1) { - return done(undefined, msg); - } else { - applyRules(msg, currentRule+1,done); - } - } - function applyRules(msg, currentRule, done) { - if (currentRule >= node.rules.length) { - return done(undefined,msg); - } - var r = node.rules[currentRule]; - if (r.t === "move") { - if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) { - applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => { - applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => { - completeApplyingRules(msg,currentRule,done); - }) - }); - } else { // 2 step move if we are moving from a child - applyRule(msg,{t:"set", p:"_temp_move", pt:r.tot, to:r.p, tot:r.pt},(err,msg)=> { - applyRule(msg,{t:"delete", p:r.p, pt:r.pt},(err,msg)=> { - applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:"_temp_move", tot:r.pt},(err,msg)=> { - applyRule(msg,{t:"delete", p:"_temp_move", pt:r.pt},(err,msg)=> { - completeApplyingRules(msg,currentRule,done); - }); - }); - }); - }); - } - } else { - applyRule(msg,r,(err,msg)=> { completeApplyingRules(msg,currentRule,done); }); - } - } - - if (valid) { - this.on('input', function(msg, send, done) { - applyRules(msg, 0, (err,msg) => { - if (err) { - done(err); - } else if (msg) { - send(msg); - done(); - } - }) - }); - } - } - RED.nodes.registerType("change", ChangeNode); -}; diff --git a/packages/node_modules/@node-red/nodes/core/function/16-range.html b/packages/node_modules/@node-red/nodes/core/function/16-range.html deleted file mode 100644 index f108a99ce..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/16-range.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/16-range.js b/packages/node_modules/@node-red/nodes/core/function/16-range.js deleted file mode 100644 index a5dede4ea..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/16-range.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - function RangeNode(n) { - RED.nodes.createNode(this, n); - this.action = n.action; - this.round = n.round || false; - this.minin = Number(n.minin); - this.maxin = Number(n.maxin); - this.minout = Number(n.minout); - this.maxout = Number(n.maxout); - this.property = n.property||"payload"; - var node = this; - - this.on('input', function (msg, send, done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var n = Number(value); - if (!isNaN(n)) { - if (node.action == "clamp") { - if (n < node.minin) { n = node.minin; } - if (n > node.maxin) { n = node.maxin; } - } - if (node.action == "roll") { - var divisor = node.maxin - node.minin; - n = ((n - node.minin) % divisor + divisor) % divisor + node.minin; - } - value = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout; - if (node.round) { value = Math.round(value); } - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - } - else { node.log(RED._("range.errors.notnumber")+": "+value); } - } - else { send(msg); } // If no payload - just pass it on. - done(); - }); - } - RED.nodes.registerType("range", RangeNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/80-template.html b/packages/node_modules/@node-red/nodes/core/function/80-template.html deleted file mode 100644 index 98d4a0d37..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/80-template.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/80-template.js b/packages/node_modules/@node-red/nodes/core/function/80-template.js deleted file mode 100644 index d4c27cfd0..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/80-template.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var yaml = require("js-yaml"); - - function extractTokens(tokens,set) { - set = set || new Set(); - tokens.forEach(function(token) { - if (token[0] !== 'text') { - set.add(token[1]); - if (token.length > 4) { - extractTokens(token[4],set); - } - } - }); - return set; - } - - function parseContext(key) { - var match = /^(flow|global)(\[(\w+)\])?\.(.+)/.exec(key); - if (match) { - var parts = {}; - parts.type = match[1]; - parts.store = (match[3] === '') ? "default" : match[3]; - parts.field = match[4]; - return parts; - } - return undefined; - } - - /** - * Custom Mustache Context capable to collect message property and node - * flow and global context - */ - - function NodeContext(msg, nodeContext, parent, escapeStrings, cachedContextTokens) { - this.msgContext = new mustache.Context(msg,parent); - this.nodeContext = nodeContext; - this.escapeStrings = escapeStrings; - this.cachedContextTokens = cachedContextTokens; - } - - NodeContext.prototype = new mustache.Context(); - - NodeContext.prototype.lookup = function (name) { - // try message first: - try { - var value = this.msgContext.lookup(name); - if (value !== undefined) { - if (this.escapeStrings && typeof value === "string") { - value = value.replace(/\\/g, "\\\\"); - value = value.replace(/\n/g, "\\n"); - value = value.replace(/\t/g, "\\t"); - value = value.replace(/\r/g, "\\r"); - value = value.replace(/\f/g, "\\f"); - value = value.replace(/[\b]/g, "\\b"); - } - return value; - } - - // try flow/global context: - var context = parseContext(name); - if (context) { - var type = context.type; - var store = context.store; - var field = context.field; - var target = this.nodeContext[type]; - if (target) { - return this.cachedContextTokens[name]; - } - } - return ''; - } - catch(err) { - throw err; - } - } - - NodeContext.prototype.push = function push (view) { - return new NodeContext(view, this.nodeContext, this.msgContext, undefined, this.cachedContextTokens); - }; - - function TemplateNode(n) { - RED.nodes.createNode(this,n); - this.name = n.name; - this.field = n.field || "payload"; - this.template = n.template; - this.syntax = n.syntax || "mustache"; - this.fieldType = n.fieldType || "msg"; - this.outputFormat = n.output || "str"; - - var node = this; - - function output(msg,value,send,done) { - /* istanbul ignore else */ - if (node.outputFormat === "json") { - value = JSON.parse(value); - } - /* istanbul ignore else */ - if (node.outputFormat === "yaml") { - value = yaml.load(value); - } - - if (node.fieldType === 'msg') { - RED.util.setMessageProperty(msg, node.field, value); - send(msg); - done(); - } else if ((node.fieldType === 'flow') || - (node.fieldType === 'global')) { - var context = RED.util.parseContextStore(node.field); - var target = node.context()[node.fieldType]; - target.set(context.key, value, context.store, function (err) { - if (err) { - done(err); - } else { - send(msg); - done(); - } - }); - } - } - - node.on("input", function(msg, send, done) { - - try { - /*** - * Allow template contents to be defined externally - * through inbound msg.template IFF node.template empty - */ - var template = node.template; - if (msg.hasOwnProperty("template")) { - if (template == "" || template === null) { - template = msg.template; - } - } - - if (node.syntax === "mustache") { - var is_json = (node.outputFormat === "json"); - var promises = []; - var tokens = extractTokens(mustache.parse(template)); - var resolvedTokens = {}; - tokens.forEach(function(name) { - var context = parseContext(name); - if (context) { - var type = context.type; - var store = context.store; - var field = context.field; - var target = node.context()[type]; - if (target) { - var promise = new Promise((resolve, reject) => { - target.get(field, store, (err, val) => { - if (err) { - reject(err); - } else { - resolvedTokens[name] = val; - resolve(); - } - }); - }); - promises.push(promise); - return; - } - } - }); - - Promise.all(promises).then(function() { - var value = mustache.render(template, new NodeContext(msg, node.context(), null, is_json, resolvedTokens)); - output(msg, value, send, done); - }).catch(function (err) { - done(err.message); - }); - } else { - output(msg, template, send, done); - } - } - catch(err) { - done(err.message); - } - }); - } - - RED.nodes.registerType("template",TemplateNode); - RED.library.register("templates"); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.html b/packages/node_modules/@node-red/nodes/core/function/89-delay.html deleted file mode 100644 index 3ae4637d7..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.html +++ /dev/null @@ -1,283 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.js b/packages/node_modules/@node-red/nodes/core/function/89-delay.js deleted file mode 100644 index 5205a5b18..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.js +++ /dev/null @@ -1,424 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -//Simple node to introduce a pause into a flow -module.exports = function(RED) { - "use strict"; - - var MILLIS_TO_NANOS = 1000000; - var SECONDS_TO_NANOS = 1000000000; - var _maxKeptMsgsCount; - - function maxKeptMsgsCount(node) { - if (_maxKeptMsgsCount === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _maxKeptMsgsCount = RED.settings[name]; - } - else { - _maxKeptMsgsCount = 0; - } - } - return _maxKeptMsgsCount; - } - - function DelayNode(n) { - RED.nodes.createNode(this,n); - - this.pauseType = n.pauseType; - this.timeoutUnits = n.timeoutUnits; - this.randomUnits = n.randomUnits; - this.rateUnits = n.rateUnits; - - if (n.timeoutUnits === "milliseconds") { - this.timeout = n.timeout; - } else if (n.timeoutUnits === "minutes") { - this.timeout = n.timeout * (60 * 1000); - } else if (n.timeoutUnits === "hours") { - this.timeout = n.timeout * (60 * 60 * 1000); - } else if (n.timeoutUnits === "days") { - this.timeout = n.timeout * (24 * 60 * 60 * 1000); - } else { // Default to seconds - this.timeout = n.timeout * 1000; - } - - if (n.rateUnits === "minute") { - this.rate = (60 * 1000)/n.rate; - } else if (n.rateUnits === "hour") { - this.rate = (60 * 60 * 1000)/n.rate; - } else if (n.rateUnits === "day") { - this.rate = (24 * 60 * 60 * 1000)/n.rate; - } else { // Default to seconds - this.rate = 1000/n.rate; - } - - this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1); - - if (n.randomUnits === "milliseconds") { - this.randomFirst = n.randomFirst * 1; - this.randomLast = n.randomLast * 1; - } else if (n.randomUnits === "minutes") { - this.randomFirst = n.randomFirst * (60 * 1000); - this.randomLast = n.randomLast * (60 * 1000); - } else if (n.randomUnits === "hours") { - this.randomFirst = n.randomFirst * (60 * 60 * 1000); - this.randomLast = n.randomLast * (60 * 60 * 1000); - } else if (n.randomUnits === "days") { - this.randomFirst = n.randomFirst * (24 * 60 * 60 * 1000); - this.randomLast = n.randomLast * (24 * 60 * 60 * 1000); - } else { // Default to seconds - this.randomFirst = n.randomFirst * 1000; - this.randomLast = n.randomLast * 1000; - } - - this.diff = this.randomLast - this.randomFirst; - this.name = n.name; - this.idList = []; - this.buffer = []; - this.intervalID = -1; - this.randomID = -1; - this.lastSent = null; - this.drop = n.drop; - this.droppedMsgs = 0; - this.allowrate = n.allowrate|| false; - this.fixedrate = this.rate; - this.outputs = n.outputs; - var node = this; - - function ourTimeout(handler, delay, clearHandler) { - var toutID = setTimeout(handler, delay); - return { - clear: function() { clearTimeout(toutID); clearHandler(); }, - trigger: function() { clearTimeout(toutID); return handler(); } - }; - } - - var sendMsgFromBuffer = function() { - if (node.buffer.length === 0) { - clearInterval(node.intervalID); - node.intervalID = -1; - } - if (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - if (Object.keys(msgInfo.msg).length > 1) { - msgInfo.send(msgInfo.msg); - msgInfo.done(); - } - } - node.reportDepth(); - } - - var clearDelayList = function(s) { - var len = node.idList.length; - for (var i=0; i 0) { node.status({text:node.buffer.length}); } - // else { node.status({}); } - node.status({fill:"blue",shape:"dot",text:node.buffer.length}); - node.busy = null; - }, 500); - } - } - - var loggerId = setInterval(function () { - if (node.droppedMsgs !== 0) { - node.debug("node.droppedMsgs = " + node.droppedMsgs); - node.droppedMsgs = 0; - } - }, 15 * 1000); - node.on("close", function() { clearInterval(loggerId); }); - - // The delay type modes - if (node.pauseType === "delay") { - node.on("input", function(msg, send, done) { - var id = ourTimeout(function() { - node.idList.splice(node.idList.indexOf(id),1); - if (node.timeout > 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - send(msg); - done(); - }, node.timeout, () => done()); - if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); } - else { node.idList.push(id); } - if (msg.hasOwnProperty("reset")) { clearDelayList(true); } - else if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); } - else if (node.timeout > 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - }); - node.on("close", function() { clearDelayList(); }); - } - else if (node.pauseType === "delayv") { - node.on("input", function(msg, send, done) { - var delayvar = Number(node.timeout); - if (msg.hasOwnProperty("delay") && !isNaN(parseFloat(msg.delay))) { - delayvar = parseFloat(msg.delay); - } - if (delayvar < 0) { delayvar = 0; } - var id = ourTimeout(function() { - node.idList.splice(node.idList.indexOf(id),1); - if (node.idList.length === 0) { node.status({}); } - send(msg); - if (delayvar >= 0) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - done(); - }, delayvar, () => done()); - node.idList.push(id); - if (msg.hasOwnProperty("reset")) { clearDelayList(true); } - if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); } - if (delayvar >= 0) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - }); - node.on("close", function() { clearDelayList(); }); - } - else if (node.pauseType === "random") { - node.on("input", function(msg, send, done) { - var wait = node.randomFirst + (node.diff * Math.random()); - var id = ourTimeout(function() { - node.idList.splice(node.idList.indexOf(id),1); - send(msg); - if (node.timeout >= 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - done(); - }, wait, () => done()); - if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); } - else { node.idList.push(id); } - if (msg.hasOwnProperty("reset")) { clearDelayList(true); } - if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); } - if (node.timeout >= 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - }); - node.on("close", function() { clearDelayList(); }); - } - - // The rate limit/queue type modes - else if (node.pauseType === "rate") { - node.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - if (node.intervalID !== -1 ) { - clearInterval(node.intervalID); - node.intervalID = -1; - } - delete node.lastSent; - node.buffer = []; - node.rate = node.fixedrate; - node.status({fill:"blue",shape:"ring",text:0}); - done(); - return; - } - - if (!node.drop) { - var m = RED.util.cloneMessage(msg); - delete m.flush; - delete m.lifo; - if (Object.keys(m).length > 1) { - if (node.intervalID !== -1) { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - var max_msgs = maxKeptMsgsCount(node); - if ((max_msgs > 0) && (node.buffer.length >= max_msgs)) { - node.buffer = []; - node.error(RED._("delay.errors.too-many"), msg); - } else if (msg.toFront === true) { - node.buffer.unshift({msg: m, send: send, done: done}); - node.reportDepth(); - } else { - node.buffer.push({msg: m, send: send, done: done}); - node.reportDepth(); - } - } - else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { - node.rate = msg.rate; - } - send(m); - node.reportDepth(); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - done(); - } - } - if (msg.hasOwnProperty("flush")) { - var len = node.buffer.length; - if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush),len); } - while (len > 0) { - const msgInfo = node.buffer.shift(); - if (Object.keys(msgInfo.msg).length > 1) { - node.send(msgInfo.msg); - msgInfo.done(); - } - len = len - 1; - } - if (node.buffer.length === 0) { - clearInterval(node.intervalID); - node.intervalID = -1; - } - node.status({fill:"blue",shape:"dot",text:node.buffer.length}); - done(); - } - } - else { - if (maxKeptMsgsCount(node) > 0) { - if (node.intervalID === -1) { - node.send(msg); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - if (node.buffer.length < _maxKeptMsgsCount) { - var m = RED.util.cloneMessage(msg); - node.buffer.push({msg: m, send: send, done: done}); - } else { - node.trace("dropped due to buffer overflow. msg._msgid = " + msg._msgid); - node.droppedMsgs++; - } - } - } else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { - node.rate = msg.rate; - } - var timeSinceLast; - if (node.lastSent) { - timeSinceLast = process.hrtime(node.lastSent); - } - if (!node.lastSent) { // ensuring that we always send the first message - node.lastSent = process.hrtime(); - send(msg); - } - else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) { - node.lastSent = process.hrtime(); - send(msg); - } else if (node.outputs === 2) { - send([null,msg]) - } - } - done(); - } - }); - node.on("close", function() { - clearInterval(node.intervalID); - clearTimeout(node.busy); - node.buffer.forEach((msgInfo) => msgInfo.done()); - node.buffer = []; - node.status({}); - }); - } - - // The topic based fair queue and last arrived on all topics queue - else if ((node.pauseType === "queue") || (node.pauseType === "timed")) { - node.intervalID = setInterval(function() { - if (node.pauseType === "queue") { - if (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - msgInfo.send(msgInfo.msg); // send the first on the queue - msgInfo.done(); - } - } - else { - while (node.buffer.length > 0) { // send the whole queue - const msgInfo = node.buffer.shift(); - msgInfo.send(msgInfo.msg); - msgInfo.done(); - } - } - node.reportDepth(); - },node.rate); - - var hit; - node.on("input", function(msg, send, done) { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - if (!msg.hasOwnProperty("topic")) { msg.topic = "_none_"; } - hit = false; - for (var b in node.buffer) { // check if already in queue - if (msg.topic === node.buffer[b].msg.topic) { - if (node.outputs === 2) { send([null,node.buffer[b].msg]) } - node.buffer[b].done(); - node.buffer[b] = {msg, send, done}; // if so - replace existing entry - hit = true; - break; - } - } - if (!hit) { - node.buffer.push({msg, send, done}); // if not add to end of queue - node.reportDepth(); - } - if (msg.hasOwnProperty("reset")) { - while (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - msgInfo.done(); - } - node.buffer = []; - node.rate = node.fixedrate; - node.status({text:"reset"}); - done(); - } - if (msg.hasOwnProperty("flush")) { - var len = node.buffer.length; - if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush,len)); } - while (len > 0) { - const msgInfo = node.buffer.shift(); - delete msgInfo.msg.flush; - if (Object.keys(msgInfo.msg).length > 2) { - node.send(msgInfo.msg); - msgInfo.done(); - } - len = len - 1; - } - node.status({}); - done(); - } - }); - node.on("close", function() { - clearInterval(node.intervalID); - while (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - msgInfo.done(); - } - node.buffer = []; - node.status({}); - }); - } - } - RED.nodes.registerType("delay",DelayNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html b/packages/node_modules/@node-red/nodes/core/function/89-trigger.html deleted file mode 100644 index a7ab0356f..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html +++ /dev/null @@ -1,234 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js deleted file mode 100644 index 16a00e99d..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js +++ /dev/null @@ -1,298 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - function TriggerNode(n) { - RED.nodes.createNode(this,n); - this.bytopic = n.bytopic || "all"; - this.op1 = n.op1 || "1"; - this.op2 = n.op2 || "0"; - this.op1type = n.op1type || "str"; - this.op2type = n.op2type || "str"; - this.second = (n.outputs == 2) ? true : false; - this.topic = n.topic || "topic"; - - if (this.op1type === 'val') { - if (this.op1 === 'true' || this.op1 === 'false') { - this.op1type = 'bool' - } else if (this.op1 === 'null') { - this.op1type = 'null'; - this.op1 = null; - } else { - this.op1type = 'str'; - } - } - if (this.op2type === 'val') { - if (this.op2 === 'true' || this.op2 === 'false') { - this.op2type = 'bool' - } else if (this.op2 === 'null') { - this.op2type = 'null'; - this.op2 = null; - } else { - this.op2type = 'str'; - } - } - this.extend = n.extend || "false"; - this.overrideDelay = n.overrideDelay || false; - this.units = n.units || "ms"; - this.reset = n.reset || ''; - this.duration = parseFloat(n.duration); - if (isNaN(this.duration)) { - this.duration = 250; - } - if (this.duration < 0) { - this.loop = true; - this.duration = this.duration * -1; - this.extend = false; - } - - if (this.units == "s") { this.duration = this.duration * 1000; } - if (this.units == "min") { this.duration = this.duration * 1000 * 60; } - if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; } - - this.op1Templated = (this.op1type === 'str' && this.op1.indexOf("{{") != -1); - this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1); - if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); } - if ((this.op2type === "num") && (!isNaN(this.op2))) { this.op2 = Number(this.op2); } - //if (this.op1 == "null") { this.op1 = null; } - //if (this.op2 == "null") { this.op2 = null; } - //try { this.op1 = JSON.parse(this.op1); } - //catch(e) { this.op1 = this.op1; } - //try { this.op2 = JSON.parse(this.op2); } - //catch(e) { this.op2 = this.op2; } - - var node = this; - node.topics = {}; - - var npay = {}; - var pendingMessages = []; - var activeMessagePromise = null; - var processMessageQueue = function(msgInfo) { - if (msgInfo) { - // A new message has arrived - add it to the message queue - pendingMessages.push(msgInfo); - if (activeMessagePromise !== null) { - // The node is currently processing a message, so do nothing - // more with this message - return; - } - } - if (pendingMessages.length === 0) { - // There are no more messages to process, clear the active flag - // and return - activeMessagePromise = null; - return; - } - - // There are more messages to process. Get the next message and - // start processing it. Recurse back in to check for any more - var nextMsgInfo = pendingMessages.shift(); - activeMessagePromise = processMessage(nextMsgInfo) - .then(processMessageQueue) - .catch((err) => { - nextMsgInfo.done(err); - return processMessageQueue(); - }); - } - - this.on('input', function(msg, send, done) { - processMessageQueue({msg, send, done}); - }); - - var stat = function() { - var l = Object.keys(node.topics).length; - if (l === 0) { return {} } - else if (l === 1) { return {fill:"blue",shape:"dot"} } - else return {fill:"blue",shape:"dot",text:l}; - } - - var processMessage = function(msgInfo) { - let msg = msgInfo.msg; - var topic = RED.util.getMessageProperty(msg,node.topic) || "_none"; - var promise; - var delayDuration = node.duration; - if (node.overrideDelay && msg.hasOwnProperty("delay") && !isNaN(parseFloat(msg.delay))) { - delayDuration = parseFloat(msg.delay); - } - if (node.bytopic === "all") { topic = "_none"; } - node.topics[topic] = node.topics[topic] || {}; - if (msg.hasOwnProperty("reset") || ((node.reset !== '') && msg.hasOwnProperty("payload") && (msg.payload !== null) && msg.payload.toString && (msg.payload.toString() == node.reset)) ) { - if (node.loop === true) { clearInterval(node.topics[topic].tout); } - else { clearTimeout(node.topics[topic].tout); } - delete node.topics[topic]; - node.status(stat()); - } - else { - if (node.op2type === "payl") { npay[topic] = RED.util.cloneMessage(msg); } - if (((!node.topics[topic].tout) && (node.topics[topic].tout !== 0)) || (node.loop === true)) { - promise = Promise.resolve(); - if (node.op2type === "pay") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } - else if (node.op2Templated) { node.topics[topic].m2 = mustache.render(node.op2,msg); } - else if (node.op2type !== "nul") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - node.topics[topic].m2 = value; - resolve(); - } - }); - }); - } - - return promise.then(() => { - promise = Promise.resolve(); - if (node.op1type === "pay") { } - else if (node.op1Templated) { msg.payload = mustache.render(node.op1,msg); } - else if (node.op1type !== "nul") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op1,node.op1type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - msg.payload = value; - resolve(); - } - }); - }); - } - return promise.then(() => { - if (delayDuration === 0) { node.topics[topic].tout = 0; } - else if (node.loop === true) { - /* istanbul ignore else */ - if (node.topics[topic].tout) { clearInterval(node.topics[topic].tout); } - /* istanbul ignore else */ - if (node.op1type !== "nul") { - var msg2 = RED.util.cloneMessage(msg); - node.topics[topic].tout = setInterval(function() { - if (node.op1type === "date") { msg2.payload = Date.now(); } - msgInfo.send(RED.util.cloneMessage(msg2)); - }, delayDuration); - } - } - else { - if (!node.topics[topic].tout) { - node.topics[topic].tout = setTimeout(function() { - var msg2 = null; - if (node.op2type !== "nul") { - var promise = Promise.resolve(); - msg2 = RED.util.cloneMessage(msg); - if (node.op2type === "flow" || node.op2type === "global") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - node.topics[topic].m2 = value; - resolve(); - } - }); - }); - } - promise.then(() => { - if (node.op2type === "payl") { - if (node.second === true) { msgInfo.send([null,npay[topic]]); } - else { msgInfo.send(npay[topic]); } - delete npay[topic]; - } - else { - msg2.payload = node.topics[topic].m2; - if (node.op2type === "date") { msg2.payload = Date.now(); } - if (node.second === true) { msgInfo.send([null,msg2]); } - else { msgInfo.send(msg2); } - } - delete node.topics[topic]; - node.status(stat()); - }).catch(err => { - node.error(err); - }); - } else { - delete node.topics[topic]; - node.status(stat()); - } - - }, delayDuration); - } - } - msgInfo.done(); - node.status(stat()); - if (node.op1type !== "nul") { msgInfo.send(RED.util.cloneMessage(msg)); } - }); - }); - } - else if ((node.extend === "true" || node.extend === true) && (delayDuration > 0)) { - /* istanbul ignore else */ - if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } - /* istanbul ignore else */ - if (node.topics[topic].tout) { clearTimeout(node.topics[topic].tout); } - node.topics[topic].tout = setTimeout(function() { - var msg2 = null; - var promise = Promise.resolve(); - - if (node.op2type !== "nul") { - if (node.op2type === "flow" || node.op2type === "global") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - node.topics[topic].m2 = value; - resolve(); - } - }); - }); - } - } - promise.then(() => { - if (node.op2type !== "nul") { - if (node.topics[topic] !== undefined) { - msg2 = RED.util.cloneMessage(msg); - msg2.payload = node.topics[topic].m2; - } - } - delete node.topics[topic]; - node.status(stat()); - if (node.second === true) { msgInfo.send([null,msg2]); } - else { msgInfo.send(msg2); } - }).catch(err => { - node.error(err); - }); - }, delayDuration); - } - // else { - // if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } - // } - } - msgInfo.done(); - return Promise.resolve(); - } - this.on("close", function() { - for (var t in node.topics) { - /* istanbul ignore else */ - if (node.topics[t]) { - if (node.loop === true) { clearInterval(node.topics[t].tout); } - else { clearTimeout(node.topics[t].tout); } - delete node.topics[t]; - } - } - node.status(stat()); - }); - } - RED.nodes.registerType("trigger",TriggerNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/90-exec.html b/packages/node_modules/@node-red/nodes/core/function/90-exec.html deleted file mode 100644 index ea988a84a..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/90-exec.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/90-exec.js b/packages/node_modules/@node-red/nodes/core/function/90-exec.js deleted file mode 100644 index cf4168ae8..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/90-exec.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var spawn = require('child_process').spawn; - var exec = require('child_process').exec; - var fs = require('fs'); - var isUtf8 = require('is-utf8'); - - function ExecNode(n) { - RED.nodes.createNode(this,n); - this.cmd = (n.command || "").trim(); - if (n.addpay === undefined) { n.addpay = true; } - this.addpay = n.addpay; - if (this.addpay === true) { - this.addpay = "payload"; - } - this.append = (n.append || "").trim(); - this.useSpawn = (n.useSpawn == "true"); - this.timer = Number(n.timer || 0)*1000; - this.activeProcesses = {}; - this.oldrc = (n.oldrc || false).toString(); - this.execOpt = {encoding:'binary', maxBuffer:RED.settings.execMaxBufferSize||10000000, windowsHide: (n.winHide === true)}; - this.spawnOpt = {windowsHide: (n.winHide === true) } - var node = this; - - if (process.platform === 'linux' && fs.existsSync('/bin/bash')) { node.execOpt.shell = '/bin/bash'; } - - var cleanup = function(p) { - node.activeProcesses[p].kill(); - //node.status({fill:"red",shape:"dot",text:"timeout"}); - //node.error("Exec node timeout"); - } - - this.on("input", function(msg, nodeSend, nodeDone) { - if (msg.hasOwnProperty("kill")) { - if (typeof msg.kill !== "string" || msg.kill.length === 0 || !msg.kill.toUpperCase().startsWith("SIG") ) { msg.kill = "SIGTERM"; } - if (msg.hasOwnProperty("pid")) { - if (node.activeProcesses.hasOwnProperty(msg.pid) ) { - node.activeProcesses[msg.pid].kill(msg.kill.toUpperCase()); - node.status({fill:"red",shape:"dot",text:"killed"}); - } - } - else { - if (Object.keys(node.activeProcesses).length === 1) { - node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill(msg.kill.toUpperCase()); - node.status({fill:"red",shape:"dot",text:"killed"}); - } - } - nodeDone(); - } - else { - var child; - // make the extra args into an array - // then prepend with the msg.payload - var arg = node.cmd; - if (node.addpay) { - var value = RED.util.getMessageProperty(msg, node.addpay); - if (value !== undefined) { - arg += " " + value; - } - } - if (node.append.trim() !== "") { arg += " " + node.append; } - if (this.useSpawn === true) { - // slice whole line by spaces and removes any quotes since spawn can't handle them - arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g).map((a) => { - if (/^".*"$/.test(a)) { - return a.slice(1,-1) - } else { - return a - } - }); - var cmd = arg.shift(); - /* istanbul ignore else */ - if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); } - child = spawn(cmd,arg,node.spawnOpt); - node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid}); - var unknownCommand = (child.pid === undefined); - if (node.timer !== 0) { - child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer); - } - node.activeProcesses[child.pid] = child; - child.stdout.on('data', function (data) { - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - // console.log('[exec] stdout: ' + data,child.pid); - if (isUtf8(data)) { msg.payload = data.toString(); } - else { msg.payload = data; } - nodeSend([RED.util.cloneMessage(msg),null,null]); - } - }); - child.stderr.on('data', function (data) { - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - if (isUtf8(data)) { msg.payload = data.toString(); } - else { msg.payload = Buffer.from(data); } - nodeSend([null,RED.util.cloneMessage(msg),null]); - } - }); - child.on('close', function (code,signal) { - if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) { - delete node.activeProcesses[child.pid]; - if (child.tout) { clearTimeout(child.tout); } - msg.payload = code; - if (node.oldrc === "false") { - msg.payload = {code:code}; - if (signal) { msg.payload.signal = signal; } - } - if (code === 0) { node.status({}); } - if (code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } - else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc:"+code}); } - else { node.status({fill:"yellow",shape:"dot",text:"rc:"+code}); } - nodeSend([null,null,RED.util.cloneMessage(msg)]); - } - nodeDone(); - }); - child.on('error', function (code) { - if (child.tout) { clearTimeout(child.tout); } - delete node.activeProcesses[child.pid]; - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - node.error(code,RED.util.cloneMessage(msg)); - } - }); - } - else { - /* istanbul ignore else */ - if (RED.settings.verbose) { node.log(arg); } - child = exec(arg, node.execOpt, function (error, stdout, stderr) { - var msg2, msg3; - delete msg.payload; - if (stderr) { - msg2 = RED.util.cloneMessage(msg); - msg2.payload = stderr; - } - msg.payload = Buffer.from(stdout,"binary"); - if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); } - node.status({}); - //console.log('[exec] stdout: ' + stdout); - //console.log('[exec] stderr: ' + stderr); - if (error !== null) { - msg3 = RED.util.cloneMessage(msg); - msg3.payload = {code:error.code, message:error.message}; - if (error.signal) { msg3.payload.signal = error.signal; } - if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } - else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); } - if (RED.settings.verbose) { node.log('error:' + error); } - } - else if (node.oldrc === "false") { - msg3 = RED.util.cloneMessage(msg); - msg3.payload = {code:0}; - } - if (!msg3) { node.status({}); } - else { - msg.rc = msg3.payload; - if (msg2) { msg2.rc = msg3.payload; } - } - nodeSend([msg,msg2,msg3]); - if (child.tout) { clearTimeout(child.tout); } - delete node.activeProcesses[child.pid]; - nodeDone(); - }); - node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid}); - child.on('error',function() {}); - if (node.timer !== 0) { - child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer); - } - node.activeProcesses[child.pid] = child; - } - } - }); - - this.on('close',function() { - for (var pid in node.activeProcesses) { - /* istanbul ignore else */ - if (node.activeProcesses.hasOwnProperty(pid)) { - if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); } - // console.log("KILLING",pid); - var process = node.activeProcesses[pid]; - node.activeProcesses[pid] = null; - process.kill(); - } - } - node.activeProcesses = {}; - node.status({}); - }); - } - RED.nodes.registerType("exec",ExecNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/rbe.html b/packages/node_modules/@node-red/nodes/core/function/rbe.html deleted file mode 100644 index 11ff18860..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/rbe.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/rbe.js b/packages/node_modules/@node-red/nodes/core/function/rbe.js deleted file mode 100644 index 4548a8c6a..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/rbe.js +++ /dev/null @@ -1,97 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - function RbeNode(n) { - RED.nodes.createNode(this,n); - this.func = n.func || "rbe"; - this.gap = n.gap || "0"; - this.start = n.start || ''; - this.inout = n.inout || "out"; - this.pc = false; - if (this.gap.substr(-1) === "%") { - this.pc = true; - this.gap = parseFloat(this.gap); - } - this.g = this.gap; - this.property = n.property || "payload"; - this.topi = n.topi || "topic"; - this.septopics = true; - if (n.septopics !== undefined && n.septopics === false) { - this.septopics = false; - } - - var node = this; - - node.previous = {}; - this.on("input",function(msg) { - var topic; - try { - topic = RED.util.getMessageProperty(msg,node.topi); - } - catch(e) { } - if (msg.hasOwnProperty("reset")) { - if (node.septopics && topic && (typeof topic === "string") && (topic !== "")) { - delete node.previous[msg.topic]; - } - else { node.previous = {}; } - } - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var t = "_no_topic"; - if (node.septopics) { t = topic || t; } - if ((this.func === "rbe") || (this.func === "rbei")) { - var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false; - if (typeof(value) === "object") { - if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; } - if (!RED.util.compareObjects(value, node.previous[t])) { - node.previous[t] = RED.util.cloneMessage(value); - if (doSend) { node.send(msg); } - } - } - else { - if (value !== node.previous[t]) { - node.previous[t] = RED.util.cloneMessage(value); - if (doSend) { node.send(msg); } - } - } - } - else { - var n = parseFloat(value); - if (!isNaN(n)) { - if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband")) { - if (node.start === '') { node.previous[t] = n; } - else { node.previous[t] = node.start; } - } - if (node.pc) { node.gap = Math.abs(node.previous[t] * node.g / 100) || 0; } - else { node.gap = Number(node.gap); } - if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; } - if (node.previous[t] === undefined) { node.previous[t] = n - node.gap - 1; } - if (Math.abs(n - node.previous[t]) === node.gap) { - if ((this.func === "deadbandEq")||(this.func === "narrowband")) { - if (node.inout === "out") { node.previous[t] = n; } - node.send(msg); - } - } - else if (Math.abs(n - node.previous[t]) > node.gap) { - if (this.func === "deadband" || this.func === "deadbandEq") { - if (node.inout === "out") { node.previous[t] = n; } - node.send(msg); - } - } - else if (Math.abs(n - node.previous[t]) < node.gap) { - if ((this.func === "narrowband")||(this.func === "narrowbandEq")) { - if (node.inout === "out") { node.previous[t] = n; } - node.send(msg); - } - } - if (node.inout === "in") { node.previous[t] = n; } - } - else { - node.warn(RED._("rbe.warn.nonumber")); - } - } - } // ignore msg with no payload property. - }); - } - RED.nodes.registerType("rbe",RbeNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/05-tls.html b/packages/node_modules/@node-red/nodes/core/network/05-tls.html deleted file mode 100644 index d09860843..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/05-tls.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/05-tls.js b/packages/node_modules/@node-red/nodes/core/network/05-tls.js deleted file mode 100644 index 888d749fd..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/05-tls.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var fs = require('fs'); -module.exports = function(RED) { - "use strict"; - - function TLSConfig(n) { - RED.nodes.createNode(this,n); - this.valid = true; - this.verifyservercert = n.verifyservercert; - var certPath = n.cert.trim(); - var keyPath = n.key.trim(); - var caPath = n.ca.trim(); - this.servername = (n.servername||"").trim(); - this.alpnprotocol = (n.alpnprotocol||"").trim(); - - if ((certPath.length > 0) || (keyPath.length > 0) || (caPath.length > 0)) { - - if ( (certPath.length > 0) !== (keyPath.length > 0)) { - this.valid = false; - this.error(RED._("tls.error.missing-file")); - return; - } - - try { - if (certPath) { - this.cert = fs.readFileSync(certPath); - } - if (keyPath) { - this.key = fs.readFileSync(keyPath); - } - if (caPath) { - this.ca = fs.readFileSync(caPath); - } - } catch(err) { - this.valid = false; - this.error(err.toString()); - return; - } - } else { - if (this.credentials) { - var certData = this.credentials.certdata || ""; - var keyData = this.credentials.keydata || ""; - var caData = this.credentials.cadata || ""; - - if ((certData.length > 0) !== (keyData.length > 0)) { - this.valid = false; - this.error(RED._("tls.error.missing-file")); - return; - } - - if (certData) { - this.cert = certData; - } - if (keyData) { - this.key = keyData; - } - if (caData) { - this.ca = caData; - } - } - } - } - RED.nodes.registerType("tls-config", TLSConfig, { - credentials: { - certdata: {type:"text"}, - keydata: {type:"text"}, - cadata: {type:"text"}, - passphrase: {type:"password"} - }, - settings: { - tlsConfigDisableLocalFiles: { - value: false, - exportable: true - } - } - }); - - TLSConfig.prototype.addTLSOptions = function(opts) { - if (this.valid) { - if (this.key) { - opts.key = this.key; - } - if (this.cert) { - opts.cert = this.cert; - } - if (this.ca) { - opts.ca = this.ca; - } - if (this.credentials && this.credentials.passphrase) { - opts.passphrase = this.credentials.passphrase; - } - if (this.servername) { - opts.servername = this.servername; - } - if (this.alpnprotocol) { - opts.ALPNProtocols = [this.alpnprotocol]; - } - opts.rejectUnauthorized = this.verifyservercert; - } - return opts; - } - -} diff --git a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html b/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html deleted file mode 100644 index 85939a1ef..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js b/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js deleted file mode 100644 index abcee66f6..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - 'use strict'; - - function HTTPProxyConfig(n) { - RED.nodes.createNode(this, n); - this.name = n.name; - this.url = n.url; - this.noproxy = n.noproxy; - }; - - RED.nodes.registerType('http proxy', HTTPProxyConfig, { - credentials: { - username: {type:'text'}, - password: {type:'password'} - } - }); -}; diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html deleted file mode 100644 index 747281ad0..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html +++ /dev/null @@ -1,908 +0,0 @@ - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js deleted file mode 100644 index 5d4efaf93..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js +++ /dev/null @@ -1,1139 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var mqtt = require("mqtt"); - var isUtf8 = require('is-utf8'); - var HttpsProxyAgent = require('https-proxy-agent'); - var url = require('url'); - - //#region "Supporting functions" - function matchTopic(ts,t) { - if (ts == "#") { - return true; - } - /* The following allows shared subscriptions (as in MQTT v5) - http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.html#_Toc514345522 - - 4.8.2 describes shares like: - $share/{ShareName}/{filter} - $share is a literal string that marks the Topic Filter as being a Shared Subscription Topic Filter. - {ShareName} is a character string that does not include "/", "+" or "#" - {filter} The remainder of the string has the same syntax and semantics as a Topic Filter in a non-shared subscription. Refer to section 4.7. - */ - else if(ts.startsWith("$share")){ - ts = ts.replace(/^\$share\/[^#+/]+\/(.*)/g,"$1"); - } - var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); - return re.test(t); - } - - /** - * Helper function for setting integer property values in the MQTT V5 properties object - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {integer} [minVal] The minimum value. If the src value is less than minVal, it will NOT be set in the destination - * @param {integer} [maxVal] The maximum value. If the src value is greater than maxVal, it will NOT be set in the destination - * @param {integer} [def] An optional default to set in the destination object if prop is NOT present in the soruce object - */ - function setIntProp(src, dst, propName, minVal, maxVal, def) { - if (hasProperty(src, propName)) { - var v = parseInt(src[propName]); - if(isNaN(v)) return; - if(minVal != null) { - if(v < minVal) return; - } - if(maxVal != null) { - if(v > maxVal) return; - } - dst[propName] = v; - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Test a topic string is valid - * @param {string} topic - * @returns `true` if it is a valid topic - */ - function isValidSubscriptionTopic(topic) { - return /^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(topic) - } - - /** - * Helper function for setting string property values in the MQTT V5 properties object - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {string} [def] An optional default to set in the destination object if prop is NOT present in the soruce object - */ - function setStrProp(src, dst, propName, def) { - if (src[propName] && typeof src[propName] == "string") { - dst[propName] = src[propName]; - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Helper function for setting boolean property values in the MQTT V5 properties object - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {boolean} [def] An optional default to set in the destination object if prop is NOT present in the soruce object - */ - function setBoolProp(src, dst, propName, def) { - if (src[propName] != null) { - if(src[propName] === "true" || src[propName] === true) { - dst[propName] = true; - } else if(src[propName] === "false" || src[propName] === false) { - dst[propName] = true; - } - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Helper function for copying the MQTT v5 srcUserProperties object (parameter1) to the properties object (parameter2). - * Any property in srcUserProperties that is NOT a key/string pair will be silently discarded. - * NOTE: if no sutable properties are present, the userProperties object will NOT be added to the properties object - * @param {object} srcUserProperties An object with key/value string pairs - * @param {object} properties A properties object in which userProperties will be copied to - */ - function setUserProperties(srcUserProperties, properties) { - if (srcUserProperties && typeof srcUserProperties == "object") { - let _clone = {}; - let count = 0; - let keys = Object.keys(srcUserProperties); - if(!keys || !keys.length) return null; - keys.forEach(key => { - let val = srcUserProperties[key]; - if(typeof val == "string") { - count++; - _clone[key] = val; - } - }); - if(count) properties.userProperties = _clone; - } - } - - /** - * Helper function for copying the MQTT v5 buffer type properties - * NOTE: if src[propName] is not a buffer, dst[propName] will NOT be assigned a value (unless def is set) - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {boolean} [def] An optional default to set in the destination object if prop is NOT present in the Source object - */ - function setBufferProp(src, dst, propName, def) { - if(!dst) return; - if (src && dst) { - var buf = src[propName]; - if (buf && typeof Buffer.isBuffer(buf)) { - dst[propName] = Buffer.from(buf); - } - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Helper function for applying changes to an objects properties ONLY when the src object actually has the property. - * This avoids setting a `dst` property null/undefined when the `src` object doesnt have the named property. - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set property - * @param {string} propName The property name to set in the Destination object - * @param {boolean} force force the dst property to be updated/created even if src property is empty - */ - function setIfHasProperty(src, dst, propName, force) { - if (src && dst && propName) { - const ok = force || hasProperty(src, propName); - if (ok) { - dst[propName] = src[propName]; - } - } - } - - /** - * Helper function to test an object has a property - * @param {object} obj Object to test - * @param {string} propName Name of property to find - * @returns true if object has property `propName` - */ - function hasProperty(obj, propName) { - //JavaScript does not protect the property name hasOwnProperty - //Object.prototype.hasOwnProperty.call is the recommended/safer test - return Object.prototype.hasOwnProperty.call(obj, propName); - } - - /** - * Handle the payload / packet recieved in MQTT In and MQTT Sub nodes - */ - function subscriptionHandler(node, datatype ,topic, payload, packet) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - - if (datatype === "buffer") { - // payload = payload; - } else if (datatype === "base64") { - payload = payload.toString('base64'); - } else if (datatype === "utf8") { - payload = payload.toString('utf8'); - } else if (datatype === "json") { - if (isUtf8(payload)) { - payload = payload.toString(); - try { payload = JSON.parse(payload); } - catch(e) { node.error(RED._("mqtt.errors.invalid-json-parse"),{payload:payload, topic:topic, qos:packet.qos, retain:packet.retain}); return; } - } - else { node.error((RED._("mqtt.errors.invalid-json-string")),{payload:payload, topic:topic, qos:packet.qos, retain:packet.retain}); return; } - } else { - if (isUtf8(payload)) { payload = payload.toString(); } - } - var msg = {topic:topic, payload:payload, qos:packet.qos, retain:packet.retain}; - if(v5 && packet.properties) { - setStrProp(packet.properties, msg, "responseTopic"); - setBufferProp(packet.properties, msg, "correlationData"); - setStrProp(packet.properties, msg, "contentType"); - setIntProp(packet.properties, msg, "messageExpiryInterval", 0); - setBoolProp(packet.properties, msg, "payloadFormatIndicator"); - setStrProp(packet.properties, msg, "reasonString"); - setUserProperties(packet.properties.userProperties, msg); - } - if ((node.brokerConn.broker === "localhost")||(node.brokerConn.broker === "127.0.0.1")) { - msg._topic = topic; - } - node.send(msg); - } - - /** - * Send an mqtt message to broker - * @param {MQTTOutNode} node the owner node - * @param {object} msg The msg to prepare for publishing - * @param {function} done callback when done - */ - function doPublish(node, msg, done) { - try { - done = typeof done == "function" ? done : function noop(){}; - let v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - const bsp = (node.brokerConn && node.brokerConn.serverProperties) || {}; - - //Sanitise the `msg` object properties ready for publishing - if (msg.qos) { - msg.qos = parseInt(msg.qos); - if ((msg.qos !== 0) && (msg.qos !== 1) && (msg.qos !== 2)) { - msg.qos = null; - } - } - - /* If node properties exists, override/set that to property in msg */ - if (node.topic) { msg.topic = node.topic; } - msg.qos = Number(node.qos || msg.qos || 0); - msg.retain = node.retain || msg.retain || false; - msg.retain = ((msg.retain === true) || (msg.retain === "true")) || false; - - if (v5) { - if (node.userProperties) { - msg.userProperties = node.userProperties; - } - if (node.responseTopic) { - msg.responseTopic = node.responseTopic; - } - if (node.correlationData) { - msg.correlationData = node.correlationData; - } - if (node.contentType) { - msg.contentType = node.contentType; - } - if (node.messageExpiryInterval) { - msg.messageExpiryInterval = node.messageExpiryInterval; - } - } - if (msg.userProperties && typeof msg.userProperties !== "object") { - delete msg.userProperties; - } - if (hasProperty(msg, "topicAlias") && !isNaN(msg.topicAlias) && (msg.topicAlias === 0 || bsp.topicAliasMaximum === 0 || msg.topicAlias > bsp.topicAliasMaximum)) { - delete msg.topicAlias; - } - - if (hasProperty(msg, "payload")) { - - //check & sanitise topic - let topicOK = hasProperty(msg, "topic") && (typeof msg.topic === "string") && (msg.topic !== ""); - - if (!topicOK && v5) { - //NOTE: A value of 0 (in server props topicAliasMaximum) indicates that the Server does not accept any Topic Aliases on this connection - if (hasProperty(msg, "topicAlias") && !isNaN(msg.topicAlias) && msg.topicAlias >= 0 && bsp.topicAliasMaximum && bsp.topicAliasMaximum >= msg.topicAlias) { - topicOK = true; - msg.topic = ""; //must be empty string - } else if (hasProperty(msg, "responseTopic") && (typeof msg.responseTopic === "string") && (msg.responseTopic !== "")) { - //TODO: if topic is empty but responseTopic has a string value, use that instead. Is this desirable? - topicOK = true; - msg.topic = msg.responseTopic; - //TODO: delete msg.responseTopic - to prevent it being resent? - } - } - topicOK = topicOK && !/[\+#]/.test(msg.topic); - - if (topicOK) { - node.brokerConn.publish(msg, done); // send the message - } else { - node.warn(RED._("mqtt.errors.invalid-topic")); - done(); - } - } else { - done(); - } - } catch (error) { - done(error); - } - } - - function setStatusDisconnected(node, allNodes) { - if(allNodes) { - for (var id in node.users) { - if (hasProperty(node.users, id)) { - node.users[id].status({ fill: "red", shape: "ring", text: "node-red:common.status.disconnected" }); - } - } - } else { - node.status({ fill: "red", shape: "ring", text: "node-red:common.status.disconnected" }); - } - } - - function setStatusConnecting(node, allNodes) { - if(allNodes) { - for (var id in node.users) { - if (hasProperty(node.users, id)) { - node.users[id].status({ fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" }); - } - } - } else { - node.status({ fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" }); - } - } - - function setStatusConnected(node, allNodes) { - if(allNodes) { - for (var id in node.users) { - if (hasProperty(node.users, id)) { - node.users[id].status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" }); - } - } - } else { - node.status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" }); - } - } - - function handleConnectAction(node, msg, done) { - let actionData = typeof msg.broker === 'object' ? msg.broker : null; - if (node.brokerConn.canConnect()) { - // Not currently connected/connecting - trigger the connect - if (actionData) { - node.brokerConn.setOptions(actionData); - } - node.brokerConn.connect(function () { - done(); - }); - } else { - // Already Connected/Connecting - if (!actionData) { - // All is good - already connected and no broker override provided - done() - } else if (actionData.force) { - // The force flag tells us to cycle the connection. - node.brokerConn.disconnect(function() { - node.brokerConn.setOptions(actionData); - node.brokerConn.connect(function () { - done(); - }); - }) - } else { - // Without force flag, we will refuse to cycle an active connection - done(new Error(RED._('mqtt.errors.invalid-action-alreadyconnected'))); - } - } - } - - function handleDisconnectAction(node, done) { - node.brokerConn.disconnect(function () { - done(); - }); - } - - //#endregion "Supporting functions" - - //#region "Broker node" - function MQTTBrokerNode(n) { - RED.nodes.createNode(this,n); - const node = this; - node.users = {}; - // Config node state - node.brokerurl = ""; - node.connected = false; - node.connecting = false; - node.closing = false; - node.options = {}; - node.queue = []; - node.subscriptions = {}; - /** @type {mqtt.MqttClient}*/ this.client; - node.setOptions = function(opts, init) { - if(!opts || typeof opts !== "object") { - return; //nothing to change, simply return - } - const originalBrokerURL = node.brokerurl; - - //apply property changes (only if the property exists in the opts object) - setIfHasProperty(opts, node, "url", init); - setIfHasProperty(opts, node, "broker", init); - setIfHasProperty(opts, node, "port", init); - setIfHasProperty(opts, node, "clientid", init); - setIfHasProperty(opts, node, "autoConnect", init); - setIfHasProperty(opts, node, "usetls", init); - setIfHasProperty(opts, node, "usews", init); - setIfHasProperty(opts, node, "verifyservercert", init); - setIfHasProperty(opts, node, "compatmode", init); - setIfHasProperty(opts, node, "protocolVersion", init); - setIfHasProperty(opts, node, "keepalive", init); - setIfHasProperty(opts, node, "cleansession", init); - setIfHasProperty(opts, node, "sessionExpiry", init); - setIfHasProperty(opts, node, "topicAliasMaximum", init); - setIfHasProperty(opts, node, "maximumPacketSize", init); - setIfHasProperty(opts, node, "receiveMaximum", init); - setIfHasProperty(opts, node, "userProperties", init);//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116 - setIfHasProperty(opts, node, "userPropertiesType", init); - - function createLWT(topic, payload, qos, retain, v5opts, v5SubPropName) { - let message = undefined; - if(topic) { - message = { - topic: topic, - payload: payload || "", - qos: Number(qos||0), - retain: retain=="true"|| retain===true, - } - if (v5opts) { - let v5Properties = message; - if(v5SubPropName) { - v5Properties = message[v5SubPropName] = {}; - } - //re-align local prop name to mqttjs std - if(hasProperty(v5opts, "respTopic")) { v5opts.responseTopic = v5opts.respTopic; } - if(hasProperty(v5opts, "correl")) { v5opts.correlationData = v5opts.correl; } - if(hasProperty(v5opts, "expiry")) { v5opts.messageExpiryInterval = v5opts.expiry; } - if(hasProperty(v5opts, "delay")) { v5opts.willDelayInterval = v5opts.delay; } - if(hasProperty(v5opts, "userProps")) { v5opts.userProperties = v5opts.userProps; } - //setup v5 properties - if(typeof v5opts.userProperties == "string" && /^ *{/.test(v5opts.userProperties)) { - try { - setUserProperties(JSON.parse(v5opts.userProps), v5Properties); - } catch(err) {} - } else if (typeof v5opts.userProperties == "object") { - setUserProperties(v5opts.userProperties, v5Properties); - } - setStrProp(v5opts, v5Properties, "contentType"); - setStrProp(v5opts, v5Properties, "responseTopic"); - setBufferProp(v5opts, v5Properties, "correlationData"); - setIntProp(v5opts, v5Properties, "messageExpiryInterval"); - setIntProp(v5opts, v5Properties, "willDelayInterval"); - } - } - return message; - } - - if(init) { - if(hasProperty(opts, "birthTopic")) { - node.birthMessage = createLWT(opts.birthTopic, opts.birthPayload, opts.birthQos, opts.birthRetain, opts.birthMsg, ""); - }; - if(hasProperty(opts, "closeTopic")) { - node.closeMessage = createLWT(opts.closeTopic, opts.closePayload, opts.closeQos, opts.closeRetain, opts.closeMsg, ""); - }; - if(hasProperty(opts, "willTopic")) { - //will v5 properties must be set in the "properties" sub object - node.options.will = createLWT(opts.willTopic, opts.willPayload, opts.willQos, opts.willRetain, opts.willMsg, "properies"); - }; - } else { - //update options - if(hasProperty(opts, "birth")) { - if(typeof opts.birth !== "object") { opts.birth = {}; } - node.birthMessage = createLWT(opts.birth.topic, opts.birth.payload, opts.birth.qos, opts.birth.retain, opts.birth.properties, ""); - } - if(hasProperty(opts, "close")) { - if(typeof opts.close !== "object") { opts.close = {}; } - node.closeMessage = createLWT(opts.close.topic, opts.close.payload, opts.close.qos, opts.close.retain, opts.close.properties, ""); - } - if(hasProperty(opts, "will")) { - if(typeof opts.will !== "object") { opts.will = {}; } - //will v5 properties must be set in the "properties" sub object - node.options.will = createLWT(opts.will.topic, opts.will.payload, opts.will.qos, opts.will.retain, opts.will.properties, "properties"); - } - } - - if (node.credentials) { - node.username = node.credentials.user; - node.password = node.credentials.password; - } - if(!init & hasProperty(opts, "username")) { - node.username = opts.username; - }; - if(!init & hasProperty(opts, "password")) { - node.password = opts.password; - }; - - // If the config node is missing certain options (it was probably deployed prior to an update to the node code), - // select/generate sensible options for the new fields - if (typeof node.usetls === 'undefined') { - node.usetls = false; - } - if (typeof node.usews === 'undefined') { - node.usews = false; - } - if (typeof node.verifyservercert === 'undefined') { - node.verifyservercert = false; - } - if (typeof node.keepalive === 'undefined') { - node.keepalive = 60; - } else if (typeof node.keepalive === 'string') { - node.keepalive = Number(node.keepalive); - } - if (typeof node.cleansession === 'undefined') { - node.cleansession = true; - } - - //use url or build a url from usetls://broker:port - if (node.url && node.brokerurl !== node.url) { - node.brokerurl = node.url; - } else { - // if the broker is ws:// or wss:// or tcp:// - if (node.broker.indexOf("://") > -1) { - node.brokerurl = node.broker; - // Only for ws or wss, check if proxy env var for additional configuration - if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) { - // check if proxy is set in env - let prox, noprox; - if (process.env.http_proxy) { prox = process.env.http_proxy; } - if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } - if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } - if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } - if (noprox) { - for (var i = 0; i < noprox.length; i += 1) { - if (node.brokerurl.indexOf(noprox[i].trim()) !== -1) { noproxy = true; } - } - } - if (prox && !noproxy) { - var parsedUrl = url.parse(node.brokerurl); - var proxyOpts = url.parse(prox); - // true for wss - proxyOpts.secureEndpoint = parsedUrl.protocol ? parsedUrl.protocol === 'wss:' : true; - // Set Agent for wsOption in MQTT - var agent = new HttpsProxyAgent(proxyOpts); - node.options.wsOptions = { - agent: agent - }; - } - } - } else { - // construct the std mqtt:// url - if (node.usetls) { - node.brokerurl = "mqtts://"; - } else { - node.brokerurl = "mqtt://"; - } - if (node.broker !== "") { - //Check for an IPv6 address - if (/(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)/.test(node.broker)) { - node.brokerurl = node.brokerurl + "[" + node.broker + "]:"; - } else { - node.brokerurl = node.brokerurl + node.broker + ":"; - } - // port now defaults to 1883 if unset. - if (!node.port) { - node.brokerurl = node.brokerurl + "1883"; - } else { - node.brokerurl = node.brokerurl + node.port; - } - } else { - node.brokerurl = node.brokerurl + "localhost:1883"; - } - } - } - - // Ensure cleansession set if clientid not supplied - if (!node.cleansession && !node.clientid) { - node.cleansession = true; - node.warn(RED._("mqtt.errors.nonclean-missingclientid")); - } - - // Build options for passing to the MQTT.js API - node.options.username = node.username; - node.options.password = node.password; - node.options.keepalive = node.keepalive; - node.options.clean = node.cleansession; - node.options.clientId = node.clientid || 'nodered_' + RED.util.generateId(); - node.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000; - delete node.options.protocolId; //V4+ default - delete node.options.protocolVersion; //V4 default - delete node.options.properties;//V5 only - if (node.compatmode == "true" || node.compatmode === true || node.protocolVersion == 3) { - node.options.protocolId = 'MQIsdp';//V3 compat only - node.options.protocolVersion = 3; - } else if ( node.protocolVersion == 5 ) { - delete node.options.protocolId; - node.options.protocolVersion = 5; - node.options.properties = {}; - node.options.properties.requestResponseInformation = true; - node.options.properties.requestProblemInformation = true; - if(node.userProperties && /^ *{/.test(node.userProperties)) { - try { - setUserProperties(JSON.parse(node.userProperties), node.options.properties); - } catch(err) {} - } - if (node.sessionExpiryInterval && node.sessionExpiryInterval !== "0") { - setIntProp(node,node.options.properties,"sessionExpiryInterval"); - } - } - if (node.usetls && n.tls) { - var tlsNode = RED.nodes.getNode(n.tls); - if (tlsNode) { - tlsNode.addTLSOptions(node.options); - } - } - - // If there's no rejectUnauthorized already, then this could be an - // old config where this option was provided on the broker node and - // not the tls node - if (typeof node.options.rejectUnauthorized === 'undefined') { - node.options.rejectUnauthorized = (node.verifyservercert == "true" || node.verifyservercert === true); - } - } - - n.autoConnect = n.autoConnect === "false" || n.autoConnect === false ? false : true; - node.setOptions(n, true); - - // Define functions called by MQTT in and out nodes - node.register = function(mqttNode) { - node.users[mqttNode.id] = mqttNode; - if (Object.keys(node.users).length === 1) { - if(node.autoConnect) { - node.connect(); - } - } - }; - - node.deregister = function(mqttNode,done) { - delete node.users[mqttNode.id]; - if (node.closing) { - return done(); - } - if (Object.keys(node.users).length === 0) { - if (node.client && node.client.connected) { - // Send close message - if (node.closeMessage) { - node.publish(node.closeMessage,function(err) { - node.client.end(done); - }); - } else { - node.client.end(done); - } - return; - } else { - if (node.client) { node.client.end(); } - return done(); - } - } - done(); - }; - node.canConnect = function() { - return !node.connected && !node.connecting; - } - node.connect = function (callback) { - if (node.canConnect()) { - node.connecting = true; - setStatusConnecting(node, true); - try { - node.serverProperties = {}; - node.client = mqtt.connect(node.brokerurl, node.options); - node.client.setMaxListeners(0); - let callbackDone = false; //prevent re-connects causing node.client.on('connect' firing callback multiple times - // Register successful connect or reconnect handler - node.client.on('connect', function (connack) { - node.connecting = false; - node.connected = true; - if(!callbackDone && typeof callback == "function") { - callback(); - } - callbackDone = true; - node.topicAliases = {}; - node.log(RED._("mqtt.state.connected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl})); - if(node.options.protocolVersion == 5 && connack && hasProperty(connack, "properties")) { - if(typeof connack.properties == "object") { - //clean & assign all props sent from server. - setIntProp(connack.properties, node.serverProperties, "topicAliasMaximum", 0); - setIntProp(connack.properties, node.serverProperties, "receiveMaximum", 0); - setIntProp(connack.properties, node.serverProperties, "sessionExpiryInterval", 0, 0xFFFFFFFF); - setIntProp(connack.properties, node.serverProperties, "maximumQoS", 0, 2); - setBoolProp(connack.properties, node.serverProperties, "retainAvailable",true); - setBoolProp(connack.properties, node.serverProperties, "wildcardSubscriptionAvailable", true); - setBoolProp(connack.properties, node.serverProperties, "subscriptionIdentifiersAvailable", true); - setBoolProp(connack.properties, node.serverProperties, "sharedSubscriptionAvailable"); - setIntProp(connack.properties, node.serverProperties, "maximumPacketSize", 0); - setIntProp(connack.properties, node.serverProperties, "serverKeepAlive"); - setStrProp(connack.properties, node.serverProperties, "responseInformation"); - setStrProp(connack.properties, node.serverProperties, "serverReference"); - setStrProp(connack.properties, node.serverProperties, "assignedClientIdentifier"); - setStrProp(connack.properties, node.serverProperties, "reasonString"); - setUserProperties(connack.properties, node.serverProperties); - } - } - setStatusConnected(node, true); - // Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection - node.client.removeAllListeners('message'); - - // Re-subscribe to stored topics - for (var s in node.subscriptions) { - if (node.subscriptions.hasOwnProperty(s)) { - let topic = s; - let qos = 0; - let _options = {}; - for (var r in node.subscriptions[s]) { - if (node.subscriptions[s].hasOwnProperty(r)) { - qos = Math.max(qos,node.subscriptions[s][r].qos); - _options = node.subscriptions[s][r].options; - node.client.on('message',node.subscriptions[s][r].handler); - } - } - _options.qos = _options.qos || qos; - node.client.subscribe(topic, _options); - } - } - - // Send any birth message - if (node.birthMessage) { - node.publish(node.birthMessage); - } - }); - node.client.on("reconnect", function() { - setStatusConnecting(node, true); - }); - //TODO: what to do with this event? Anything? Necessary? - node.client.on("disconnect", function(packet) { - //Emitted after receiving disconnect packet from broker. MQTT 5.0 feature. - var rc = packet && packet.properties && packet.properties.reasonString; - var rc = packet && packet.properties && packet.reasonCode; - //TODO: If keeping this event, do we use these? log these? - }); - // Register disconnect handlers - node.client.on('close', function () { - if (node.connected) { - node.connected = false; - node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl})); - setStatusDisconnected(node, true); - } else if (node.connecting) { - node.log(RED._("mqtt.state.connect-failed",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl})); - } - }); - - // Register connect error handler - // The client's own reconnect logic will take care of errors - node.client.on('error', function (error) { - }); - }catch(err) { - console.log(err); - } - } - }; - node.disconnect = function (callback) { - const _callback = function () { - setStatusDisconnected(node, true); - node.connecting = false; - node.connected = false; - callback && typeof callback == "function" && callback(); - }; - - if(node.client) { - if(node.client.connected && node.closeMessage) { - node.publish(node.closeMessage, function (err) { - node.client.end(_callback); - }); - } else if(node.client.connected || node.client.reconnecting) { - node.client.end(_callback); - } else if(node.client.disconnecting || node.client.connected === false) { - _callback(); - } - } else { - _callback(); - } - } - node.subscriptionIds = {}; - node.subid = 1; - node.subscribe = function (topic,options,callback,ref) { - ref = ref||0; - var qos; - if(typeof options == "object") { - qos = options.qos; - } else { - qos = options; - options = {}; - } - options.qos = qos; - if (!node.subscriptionIds[topic]) { - node.subscriptionIds[topic] = node.subid++; - } - options.properties = options.properties || {}; - options.properties.subscriptionIdentifier = node.subscriptionIds[topic]; - - node.subscriptions[topic] = node.subscriptions[topic]||{}; - var sub = { - topic:topic, - qos:qos, - options:options, - handler:function(mtopic,mpayload, mpacket) { - if(mpacket.properties && options.properties && mpacket.properties.subscriptionIdentifier && options.properties.subscriptionIdentifier && (mpacket.properties.subscriptionIdentifier !== options.properties.subscriptionIdentifier) ) { - //do nothing as subscriptionIdentifier does not match - } else if (matchTopic(topic,mtopic)) { - callback(mtopic,mpayload, mpacket); - } - }, - ref: ref - }; - node.subscriptions[topic][ref] = sub; - if (node.connected) { - node.client.on('message',sub.handler); - node.client.subscribe(topic, options); - } - }; - - node.unsubscribe = function (topic, ref, removed) { - ref = ref||0; - var sub = node.subscriptions[topic]; - if (sub) { - if (sub[ref]) { - if(node.client) { - node.client.removeListener('message',sub[ref].handler); - } - delete sub[ref]; - } - //TODO: Review. The `if(removed)` was commented out to always delete and remove subscriptions. - // if we dont then property changes dont get applied and old subs still trigger - //if (removed) { - if (Object.keys(sub).length === 0) { - delete node.subscriptions[topic]; - delete node.subscriptionIds[topic]; - if (node.connected) { - node.client.unsubscribe(topic); - } - } - //} - } - }; - node.topicAliases = {}; - - node.publish = function (msg,done) { - if (node.connected) { - if (msg.payload === null || msg.payload === undefined) { - msg.payload = ""; - } else if (!Buffer.isBuffer(msg.payload)) { - if (typeof msg.payload === "object") { - msg.payload = JSON.stringify(msg.payload); - } else if (typeof msg.payload !== "string") { - msg.payload = "" + msg.payload; - } - } - var options = { - qos: msg.qos || 0, - retain: msg.retain || false - }; - //https://github.com/mqttjs/MQTT.js/blob/master/README.md#mqttclientpublishtopic-message-options-callback - if(node.options.protocolVersion == 5) { - options.properties = options.properties || {}; - setStrProp(msg, options.properties, "responseTopic"); - setBufferProp(msg, options.properties, "correlationData"); - setStrProp(msg, options.properties, "contentType"); - setIntProp(msg, options.properties, "messageExpiryInterval", 0); - setUserProperties(msg.userProperties, options.properties); - setIntProp(msg, options.properties, "topicAlias", 1, node.serverProperties.topicAliasMaximum || 0); - setBoolProp(msg, options.properties, "payloadFormatIndicator"); - //FUTURE setIntProp(msg, options.properties, "subscriptionIdentifier", 1, 268435455); - if (options.properties.topicAlias) { - if (!node.topicAliases.hasOwnProperty(options.properties.topicAlias) && msg.topic == "") { - done("Invalid topicAlias"); - return - } - if (node.topicAliases[options.properties.topicAlias] === msg.topic) { - msg.topic = "" - } else { - node.topicAliases[options.properties.topicAlias] = msg.topic - } - } - } - - node.client.publish(msg.topic, msg.payload, options, function(err) { - done && done(err); - return - }); - } - }; - - node.on('close', function(done) { - node.closing = true; - node.disconnect(done); - }); - - } - - RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{ - credentials: { - user: {type:"text"}, - password: {type: "password"} - } - }); - //#endregion "Broker node" - - //#region "MQTTIn node" - function MQTTInNode(n) { - RED.nodes.createNode(this,n); - /**@type {MQTTInNode}*/const node = this; - /**@type {string}*/node.broker = n.broker; - /**@type {MQTTBrokerNode}*/node.brokerConn = RED.nodes.getNode(node.broker); - - node.dynamicSubs = {}; - node.isDynamic = n.hasOwnProperty("inputs") && n.inputs == 1 - node.inputs = n.inputs; - node.topic = n.topic; - node.qos = parseInt(n.qos); - node.subscriptionIdentifier = n.subscriptionIdentifier;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117 - node.nl = n.nl; - node.rap = n.rap; - node.rh = n.rh; - - const Actions = { - CONNECT: 'connect', - DISCONNECT: 'disconnect', - SUBSCRIBE: 'subscribe', - UNSUBSCRIBE: 'unsubscribe', - GETSUBS: 'getSubscriptions', - }; - const allowableActions = Object.values(Actions); - - if (isNaN(node.qos) || node.qos < 0 || node.qos > 2) { - node.qos = 2; - } - if (!node.isDynamic && !isValidSubscriptionTopic(node.topic)) { - return node.warn(RED._("mqtt.errors.invalid-topic")); - } - node.datatype = n.datatype || "utf8"; - if (node.brokerConn) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - setStatusDisconnected(node); - if (node.topic || node.isDynamic) { - node.brokerConn.register(node); - if (!node.isDynamic) { - let options = { qos: node.qos }; - if(v5) { - setIntProp(node, options, "rh", 0, 2, 0); - if(node.nl === "true" || node.nl === true) options.nl = true; - else if(node.nl === "false" || node.nl === false) options.nl = false; - if(node.rap === "true" || node.rap === true) options.rap = true; - else if(node.rap === "false" || node.rap === false) options.rap = false; - } - - node.brokerConn.subscribe(node.topic,options,function(topic, payload, packet) { - subscriptionHandler(node, node.datatype, topic, payload, packet); - },node.id); - } - if (node.brokerConn.connected) { - node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"}); - } - } - else { - node.error(RED._("mqtt.errors.not-defined")); - } - node.on('input', function (msg, send, done) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - const action = msg.action; - - if (!allowableActions.includes(action)) { - done(new Error(RED._('mqtt.errors.invalid-action-action'))); - return; - } - - if (action === Actions.CONNECT) { - handleConnectAction(node, msg, done) - } else if (action === Actions.DISCONNECT) { - handleDisconnectAction(node, done) - } else if (action === Actions.SUBSCRIBE || action === Actions.UNSUBSCRIBE) { - const subscriptions = []; - let actionData; - //coerce msg.topic into an array of strings or objects (for later iteration) - if(action === Actions.UNSUBSCRIBE && msg.topic === true) { - actionData = Object.values(node.dynamicSubs); - } else if (Array.isArray(msg.topic)) { - actionData = msg.topic; - } else if (typeof msg.topic == 'string' || typeof msg.topic == 'object') { - actionData = [msg.topic]; - } else { - done(new Error(RED._('mqtt.errors.invalid-action-badsubscription'))); - return; - } - //ensure each subscription is an object with topic etc - for (let index = 0; index < actionData.length; index++) { - let subscription = actionData[index]; - if (typeof subscription === 'string') { - subscription = { topic: subscription }; - } - if (!subscription.topic || !isValidSubscriptionTopic(subscription.topic)) { - done(new Error(RED._('mqtt.errors.invalid-topic'))); - return; - } - subscriptions.push(subscription); - } - if (action === Actions.UNSUBSCRIBE) { - subscriptions.forEach(function (sub) { - node.brokerConn.unsubscribe(sub.topic, node.id); - delete node.dynamicSubs[sub.topic]; - }) - //user can access current subscriptions through the complete node is so desired - msg.subscriptions = Object.values(node.dynamicSubs); - done(); - } else if (action === Actions.SUBSCRIBE) { - subscriptions.forEach(function (sub) { - //always unsubscribe before subscribe to prevent multiple subs to same topic - if (node.dynamicSubs[sub.topic]) { - node.brokerConn.unsubscribe(sub.topic, node.id); - delete node.dynamicSubs[sub.topic]; - } - - //prepare options. Default qos 2 & rap flag true (same as 'mqtt in' node ui defaults when adding to editor) - let options = {} - setIntProp(sub, options, 'qos', 0, 2, 2);//default to qos 2 (same as 'mqtt in' default) - sub.qos = options.qos; - if (v5) { - setIntProp(sub, options, 'rh', 0, 2, 0); //default rh to 0:send retained messages (same as 'mqtt in' default) - sub.rh = options.rh; - setBoolProp(sub, options, 'rap', true); //default rap to true:Keep retain flag of original publish (same as 'mqtt in' default) - sub.rap = options.rap; - if (sub.nl === 'true' || sub.nl === true) { - options.nl = true; - sub.nl = true; - } else if (sub.nl === 'false' || sub.nl === false) { - options.nl = false; - sub.nl = false; - } else { - delete sub.nl - } - } - - //subscribe to sub.topic & hook up subscriptionHandler - node.brokerConn.subscribe(sub.topic, options, function (topic, payload, packet) { - subscriptionHandler(node, sub.datatype || node.datatype, topic, payload, packet); - }, node.id); - node.dynamicSubs[sub.topic] = sub; //save for later unsubscription & 'list' action - }) - //user can access current subscriptions through the complete node is so desired - msg.subscriptions = Object.values(node.dynamicSubs); - done(); - } - } else if (action === Actions.GETSUBS) { - //send list of subscriptions in payload - msg.topic = "subscriptions"; - msg.payload = Object.values(node.dynamicSubs); - send(msg); - done(); - } - }); - - node.on('close', function(removed, done) { - if (node.brokerConn) { - if(node.isDynamic) { - Object.keys(node.dynamicSubs).forEach(function (topic) { - node.brokerConn.unsubscribe(topic, node.id, removed); - }); - node.dynamicSubs = {}; - } else { - node.brokerConn.unsubscribe(node.topic,node.id, removed); - } - node.brokerConn.deregister(node, done); - } - }); - } else { - node.error(RED._("mqtt.errors.missing-config")); - } - } - RED.nodes.registerType("mqtt in",MQTTInNode); - //#endregion "MQTTIn node" - - //#region "MQTTOut node" - function MQTTOutNode(n) { - RED.nodes.createNode(this,n); - const node = this; - node.topic = n.topic; - node.qos = n.qos || null; - node.retain = n.retain; - node.broker = n.broker; - node.responseTopic = n.respTopic;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901114 - node.correlationData = n.correl;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901115 - node.contentType = n.contentType;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901118 - node.messageExpiryInterval = n.expiry; //https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901112 - try { - if (/^ *{/.test(n.userProps)) { - //setup this.userProperties - setUserProperties(JSON.parse(n.userProps), node);//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116 - } - } catch(err) {} - // node.topicAlias = n.topicAlias; //https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113 - // node.payloadFormatIndicator = n.payloadFormatIndicator; //https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111 - // node.subscriptionIdentifier = n.subscriptionIdentifier;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117 - - /** @type {MQTTBrokerNode}*/node.brokerConn = RED.nodes.getNode(node.broker); - - const Actions = { - CONNECT: 'connect', - DISCONNECT: 'disconnect', - }; - - if (node.brokerConn) { - setStatusDisconnected(node); - node.on("input",function(msg,send,done) { - if (msg.action) { - if (msg.action === Actions.CONNECT) { - handleConnectAction(node, msg, done) - } else if (msg.action === Actions.DISCONNECT) { - handleDisconnectAction(node, done) - } else { - done(new Error(RED._('mqtt.errors.invalid-action-action'))); - return; - } - } else { - doPublish(node, msg, done); - } - - }); - if (node.brokerConn.connected) { - node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"}); - } - node.brokerConn.register(node); - node.on('close', function(done) { - node.brokerConn.deregister(node,done); - }); - } else { - node.error(RED._("mqtt.errors.missing-config")); - } - } - RED.nodes.registerType("mqtt out",MQTTOutNode); - //#endregion "MQTTOut node" -}; diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httpin.html b/packages/node_modules/@node-red/nodes/core/network/21-httpin.html deleted file mode 100644 index 450bd32cc..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httpin.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httpin.js b/packages/node_modules/@node-red/nodes/core/network/21-httpin.js deleted file mode 100644 index b458a459c..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httpin.js +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var bodyParser = require("body-parser"); - var multer = require("multer"); - var cookieParser = require("cookie-parser"); - var getBody = require('raw-body'); - var cors = require('cors'); - var onHeaders = require('on-headers'); - var typer = require('content-type'); - var mediaTyper = require('media-typer'); - var isUtf8 = require('is-utf8'); - var hashSum = require("hash-sum"); - - function rawBodyParser(req, res, next) { - if (req.skipRawBodyParser) { next(); } // don't parse this if told to skip - if (req._body) { return next(); } - req.body = ""; - req._body = true; - - var isText = true; - var checkUTF = false; - - if (req.headers['content-type']) { - var contentType = typer.parse(req.headers['content-type']) - if (contentType.type) { - var parsedType = mediaTyper.parse(contentType.type); - if (parsedType.type === "text") { - isText = true; - } else if (parsedType.subtype === "xml" || parsedType.suffix === "xml") { - isText = true; - } else if (parsedType.type !== "application") { - isText = false; - } else if ((parsedType.subtype !== "octet-stream") - && (parsedType.subtype !== "cbor") - && (parsedType.subtype !== "x-protobuf")) { - checkUTF = true; - } else { - // application/octet-stream or application/cbor - isText = false; - } - - } - } - - getBody(req, { - length: req.headers['content-length'], - encoding: isText ? "utf8" : null - }, function (err, buf) { - if (err) { return next(err); } - if (!isText && checkUTF && isUtf8(buf)) { - buf = buf.toString() - } - req.body = buf; - next(); - }); - } - - var corsSetup = false; - - function createRequestWrapper(node,req) { - // This misses a bunch of properties (eg headers). Before we use this function - // need to ensure it captures everything documented by Express and HTTP modules. - var wrapper = { - _req: req - }; - var toWrap = [ - "param", - "get", - "is", - "acceptsCharset", - "acceptsLanguage", - "app", - "baseUrl", - "body", - "cookies", - "fresh", - "hostname", - "ip", - "ips", - "originalUrl", - "params", - "path", - "protocol", - "query", - "route", - "secure", - "signedCookies", - "stale", - "subdomains", - "xhr", - "socket" // TODO: tidy this up - ]; - toWrap.forEach(function(f) { - if (typeof req[f] === "function") { - wrapper[f] = function() { - node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.req."+f})); - var result = req[f].apply(req,arguments); - if (result === req) { - return wrapper; - } else { - return result; - } - } - } else { - wrapper[f] = req[f]; - } - }); - - - return wrapper; - } - function createResponseWrapper(node,res) { - var wrapper = { - _res: res - }; - var toWrap = [ - "append", - "attachment", - "cookie", - "clearCookie", - "download", - "end", - "format", - "get", - "json", - "jsonp", - "links", - "location", - "redirect", - "render", - "send", - "sendfile", - "sendFile", - "sendStatus", - "set", - "status", - "type", - "vary" - ]; - toWrap.forEach(function(f) { - wrapper[f] = function() { - node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.res."+f})); - var result = res[f].apply(res,arguments); - if (result === res) { - return wrapper; - } else { - return result; - } - } - }); - return wrapper; - } - - var corsHandler = function(req,res,next) { next(); } - - if (RED.settings.httpNodeCors) { - corsHandler = cors(RED.settings.httpNodeCors); - RED.httpNode.options("*",corsHandler); - } - - function HTTPIn(n) { - RED.nodes.createNode(this,n); - if (RED.settings.httpNodeRoot !== false) { - - if (!n.url) { - this.warn(RED._("httpin.errors.missing-path")); - return; - } - this.url = n.url; - if (this.url[0] !== '/') { - this.url = '/'+this.url; - } - this.method = n.method; - this.upload = n.upload; - this.swaggerDoc = n.swaggerDoc; - - var node = this; - - this.errorHandler = function(err,req,res,next) { - node.warn(err); - res.sendStatus(500); - }; - - this.callback = function(req,res) { - var msgid = RED.util.generateId(); - res._msgid = msgid; - if (node.method.match(/^(post|delete|put|options|patch)$/)) { - node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body}); - } else if (node.method == "get") { - node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.query}); - } else { - node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res)}); - } - }; - - var httpMiddleware = function(req,res,next) { next(); } - - if (RED.settings.httpNodeMiddleware) { - if (typeof RED.settings.httpNodeMiddleware === "function" || Array.isArray(RED.settings.httpNodeMiddleware)) { - httpMiddleware = RED.settings.httpNodeMiddleware; - } - } - - var maxApiRequestSize = RED.settings.apiMaxLength || '5mb'; - var jsonParser = bodyParser.json({limit:maxApiRequestSize}); - var urlencParser = bodyParser.urlencoded({limit:maxApiRequestSize,extended:true}); - - var metricsHandler = function(req,res,next) { next(); } - if (this.metric()) { - metricsHandler = function(req, res, next) { - var startAt = process.hrtime(); - onHeaders(res, function() { - if (res._msgid) { - var diff = process.hrtime(startAt); - var ms = diff[0] * 1e3 + diff[1] * 1e-6; - var metricResponseTime = ms.toFixed(3); - var metricContentLength = res.getHeader("content-length"); - //assuming that _id has been set for res._metrics in HttpOut node! - node.metric("response.time.millis", {_msgid:res._msgid} , metricResponseTime); - node.metric("response.content-length.bytes", {_msgid:res._msgid} , metricContentLength); - } - }); - next(); - }; - } - - var multipartParser = function(req,res,next) { next(); } - if (this.upload) { - var mp = multer({ storage: multer.memoryStorage() }).any(); - multipartParser = function(req,res,next) { - mp(req,res,function(err) { - req._body = true; - next(err); - }) - }; - } - - if (this.method == "get") { - RED.httpNode.get(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,this.callback,this.errorHandler); - } else if (this.method == "post") { - RED.httpNode.post(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,multipartParser,rawBodyParser,this.callback,this.errorHandler); - } else if (this.method == "put") { - RED.httpNode.put(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); - } else if (this.method == "patch") { - RED.httpNode.patch(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); - } else if (this.method == "delete") { - RED.httpNode.delete(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); - } - - this.on("close",function() { - var node = this; - RED.httpNode._router.stack.forEach(function(route,i,routes) { - if (route.route && route.route.path === node.url && route.route.methods[node.method]) { - routes.splice(i,1); - } - }); - }); - } else { - this.warn(RED._("httpin.errors.not-created")); - } - } - RED.nodes.registerType("http in",HTTPIn); - - - function HTTPOut(n) { - RED.nodes.createNode(this,n); - var node = this; - this.headers = n.headers||{}; - this.statusCode = n.statusCode; - this.on("input",function(msg,_send,done) { - if (msg.res) { - var headers = RED.util.cloneMessage(node.headers); - if (msg.headers) { - if (msg.headers.hasOwnProperty('x-node-red-request-node')) { - var headerHash = msg.headers['x-node-red-request-node']; - delete msg.headers['x-node-red-request-node']; - var hash = hashSum(msg.headers); - if (hash === headerHash) { - delete msg.headers; - } - } - if (msg.headers) { - for (var h in msg.headers) { - if (msg.headers.hasOwnProperty(h) && !headers.hasOwnProperty(h)) { - headers[h] = msg.headers[h]; - } - } - } - } - if (Object.keys(headers).length > 0) { - msg.res._res.set(headers); - } - if (msg.cookies) { - for (var name in msg.cookies) { - if (msg.cookies.hasOwnProperty(name)) { - if (msg.cookies[name] === null || msg.cookies[name].value === null) { - if (msg.cookies[name]!==null) { - msg.res._res.clearCookie(name,msg.cookies[name]); - } else { - msg.res._res.clearCookie(name); - } - } else if (typeof msg.cookies[name] === 'object') { - msg.res._res.cookie(name,msg.cookies[name].value,msg.cookies[name]); - } else { - msg.res._res.cookie(name,msg.cookies[name]); - } - } - } - } - var statusCode = node.statusCode || msg.statusCode || 200; - if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) { - msg.res._res.status(statusCode).jsonp(msg.payload); - } else { - if (msg.res._res.get('content-length') == null) { - var len; - if (msg.payload == null) { - len = 0; - } else if (Buffer.isBuffer(msg.payload)) { - len = msg.payload.length; - } else if (typeof msg.payload == "number") { - len = Buffer.byteLength(""+msg.payload); - } else { - len = Buffer.byteLength(msg.payload); - } - msg.res._res.set('content-length', len); - } - - if (typeof msg.payload === "number") { - msg.payload = ""+msg.payload; - } - msg.res._res.status(statusCode).send(msg.payload); - } - } else { - node.warn(RED._("httpin.errors.no-response")); - } - done(); - }); - } - RED.nodes.registerType("http response",HTTPOut); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html deleted file mode 100644 index bf1f76b7f..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js deleted file mode 100644 index e9bf49d68..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js +++ /dev/null @@ -1,673 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - const got = require("got"); - const {CookieJar} = require("tough-cookie"); - const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent'); - const FormData = require('form-data'); - const { v4: uuid } = require('uuid'); - const crypto = require('crypto'); - const URL = require("url").URL - var mustache = require("mustache"); - var querystring = require("querystring"); - var cookie = require("cookie"); - var hashSum = require("hash-sum"); - - - // Cache a reference to the existing https.request function - // so we can compare later to see if an old agent-base instance - // has been required. - // This is generally okay as the core nodes are required before - // any contrib nodes. Where it will fail is if the agent-base module - // is required via the settings file or outside of Node-RED before it - // is started. - // If there are other modules that patch the function, they will get undone - // as well. Not much we can do about that right now. Patching core - // functions is bad. - const HTTPS_MODULE = require("https"); - const HTTPS_REQUEST = HTTPS_MODULE.request; - - function checkNodeAgentPatch() { - if (HTTPS_MODULE.request !== HTTPS_REQUEST && HTTPS_MODULE.request.length === 2) { - RED.log.warn(` - ---------------------------------------------------------------------- -Patched https.request function detected. This will break the -HTTP Request node. The original code has now been restored. - -This is likely caused by a contrib node including an old version of -the 'agent-base@<5.0.0' module. - -You can identify what node is at fault by running: - npm list agent-base -in your Node-RED user directory (${RED.settings.userDir}). ---------------------------------------------------------------------- -`); - HTTPS_MODULE.request = HTTPS_REQUEST - } - } - - function HTTPRequest(n) { - RED.nodes.createNode(this,n); - checkNodeAgentPatch(); - var node = this; - var nodeUrl = n.url; - var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; - var nodeMethod = n.method || "GET"; - var paytoqs = false; - var paytobody = false; - var redirectList = []; - var sendErrorsToCatch = n.senderr; - - var nodeHTTPPersistent = n["persist"]; - if (n.tls) { - var tlsNode = RED.nodes.getNode(n.tls); - } - this.ret = n.ret || "txt"; - this.authType = n.authType || "basic"; - if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; } - else { this.reqTimeout = 120000; } - - if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; } - else if (n.paytoqs === "body") { paytobody = true; } - - - var prox, noprox; - if (process.env.http_proxy) { prox = process.env.http_proxy; } - if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } - if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } - if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } - - var proxyConfig = null; - if (n.proxy) { - proxyConfig = RED.nodes.getNode(n.proxy); - prox = proxyConfig.url; - noprox = proxyConfig.noproxy; - } - - let timingLog = false; - if (RED.settings.hasOwnProperty("httpRequestTimingLog")) { - timingLog = RED.settings.httpRequestTimingLog; - } - - this.on("input",function(msg,nodeSend,nodeDone) { - checkNodeAgentPatch(); - //reset redirectList on each request - redirectList = []; - var preRequestTimestamp = process.hrtime(); - node.status({fill:"blue",shape:"dot",text:"httpin.status.requesting"}); - var url = nodeUrl || msg.url; - if (msg.url && nodeUrl && (nodeUrl !== msg.url)) { // revert change below when warning is finally removed - node.warn(RED._("common.errors.nooverride")); - } - - if (isTemplatedUrl) { - url = mustache.render(nodeUrl,msg); - } - if (!url) { - node.error(RED._("httpin.errors.no-url"),msg); - nodeDone(); - return; - } - - - // url must start http:// or https:// so assume http:// if not set - if (url.indexOf("://") !== -1 && url.indexOf("http") !== 0) { - node.warn(RED._("httpin.errors.invalid-transport")); - node.status({fill:"red",shape:"ring",text:"httpin.errors.invalid-transport"}); - nodeDone(); - return; - } - if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) { - if (tlsNode) { - url = "https://"+url; - } else { - url = "http://"+url; - } - } - - // The Request module used in Node-RED 1.x was tolerant of query strings that - // were partially encoded. For example - "?a=hello%20there&b=20%" - // The GOT module doesn't like that. - // The following is an attempt to normalise the url to ensure it is properly - // encoded. We cannot just encode it directly as we don't want any valid - // encoded entity to end up doubly encoded. - if (url.indexOf("?") > -1) { - // Only do this if there is a query string to deal with - const [hostPath, ...queryString] = url.split("?") - const query = queryString.join("?"); - if (query) { - // Look for any instance of % not followed by two hex chars. - // Replace any we find with %25. - const escapedQueryString = query.replace(/(%.?.?)/g, function(v) { - if (/^%[a-f0-9]{2}/i.test(v)) { - return v; - } - return v.replace(/%/,"%25") - }) - url = hostPath+"?"+escapedQueryString; - } - } - - var method = nodeMethod.toUpperCase() || "GET"; - if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set - node.warn(RED._("common.errors.nooverride")); - } - if (msg.method && n.method && (n.method === "use")) { - method = msg.method.toUpperCase(); // use the msg parameter - } - - // var isHttps = (/^https/i.test(url)); - - var opts = {}; - // set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(. - // Had to remove this to get http->https redirect to work - // opts.defaultPort = isHttps?443:80; - opts.timeout = node.reqTimeout; - opts.throwHttpErrors = false; - // TODO: add UI option to auto decompress. Setting to false for 1.x compatibility - opts.decompress = false; - opts.method = method; - opts.headers = {}; - opts.retry = 0; - opts.responseType = 'buffer'; - opts.maxRedirects = 21; - opts.cookieJar = new CookieJar(); - opts.ignoreInvalidCookies = true; - opts.forever = nodeHTTPPersistent; - if (msg.requestTimeout !== undefined) { - if (isNaN(msg.requestTimeout)) { - node.warn(RED._("httpin.errors.timeout-isnan")); - } else if (msg.requestTimeout < 1) { - node.warn(RED._("httpin.errors.timeout-isnegative")); - } else { - opts.timeout = msg.requestTimeout; - } - } - const originalHeaderMap = {}; - - opts.hooks = { - beforeRequest: [ - options => { - // Whilst HTTP headers are meant to be case-insensitive, - // in the real world, there are servers that aren't so compliant. - // GOT will lower case all headers given a chance, so we need - // to restore the case of any headers the user has set. - Object.keys(options.headers).forEach(h => { - if (originalHeaderMap[h] && originalHeaderMap[h] !== h) { - options.headers[originalHeaderMap[h]] = options.headers[h]; - delete options.headers[h]; - } - }) - } - ], - beforeRedirect: [ - (options, response) => { - let redirectInfo = { - location: response.headers.location - } - if (response.headers.hasOwnProperty('set-cookie')) { - redirectInfo.cookies = extractCookies(response.headers['set-cookie']); - } - redirectList.push(redirectInfo) - } - ] - } - - var ctSet = "Content-Type"; // set default camel case - var clSet = "Content-Length"; - if (msg.headers) { - if (msg.headers.hasOwnProperty('x-node-red-request-node')) { - var headerHash = msg.headers['x-node-red-request-node']; - delete msg.headers['x-node-red-request-node']; - var hash = hashSum(msg.headers); - if (hash === headerHash) { - delete msg.headers; - } - } - if (msg.headers) { - for (var v in msg.headers) { - if (msg.headers.hasOwnProperty(v)) { - var name = v.toLowerCase(); - if (name !== "content-type" && name !== "content-length") { - // only normalise the known headers used later in this - // function. Otherwise leave them alone. - name = v; - } - else if (name === 'content-type') { ctSet = v; } - else { clSet = v; } - opts.headers[name] = msg.headers[v]; - } - } - } - } - - if (msg.hasOwnProperty('followRedirects')) { - opts.followRedirect = !!msg.followRedirects; - } - - if (opts.headers.hasOwnProperty('cookie')) { - var cookies = cookie.parse(opts.headers.cookie, {decode:String}); - for (var name in cookies) { - opts.cookieJar.setCookie(cookie.serialize(name, cookies[name], {encode:String}), url, {ignoreError: true}); - } - delete opts.headers.cookie; - } - if (msg.cookies) { - for (var name in msg.cookies) { - if (msg.cookies.hasOwnProperty(name)) { - if (msg.cookies[name] === null || msg.cookies[name].value === null) { - // This case clears a cookie for HTTP In/Response nodes. - // Ignore for this node. - } else if (typeof msg.cookies[name] === 'object') { - if(msg.cookies[name].encode === false){ - // If the encode option is false, the value is not encoded. - opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name].value, {encode: String}), url, {ignoreError: true}); - } else { - // The value is encoded by encodeURIComponent(). - opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name].value), url, {ignoreError: true}); - } - } else { - opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true}); - } - } - } - } - var parsedURL = new URL(url) - this.credentials = this.credentials || {} - if (parsedURL.username && !this.credentials.user) { - this.credentials.user = parsedURL.username - } - if (parsedURL.password && !this.credentials.password) { - this.credentials.password = parsedURL.password - } - if (Object.keys(this.credentials).length != 0) { - if (this.authType === "basic") { - // Workaround for https://github.com/sindresorhus/got/issues/1169 (fixed in got v12) - // var cred = "" - if (this.credentials.user || this.credentials.password) { - // cred = `${this.credentials.user}:${this.credentials.password}`; - opts.headers.Authorization = "Basic " + Buffer.from(`${this.credentials.user}:${this.credentials.password}`).toString("base64"); - } - // build own basic auth header - // opts.headers.Authorization = "Basic " + Buffer.from(cred).toString("base64"); - } else if (this.authType === "digest") { - let digestCreds = this.credentials; - let sentCreds = false; - opts.hooks.afterResponse = [(response, retry) => { - if (response.statusCode === 401) { - if (sentCreds) { - return response - } - const requestUrl = new URL(response.request.requestUrl); - const options = response.request.options; - const normalisedHeaders = {}; - Object.keys(response.headers).forEach(k => { - normalisedHeaders[k.toLowerCase()] = response.headers[k] - }) - if (normalisedHeaders['www-authenticate']) { - let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, options.method, requestUrl.pathname, normalisedHeaders['www-authenticate']) - options.headers.Authorization = authHeader; - } - sentCreds = true; - return retry(options); - } - return response - }]; - } else if (this.authType === "bearer") { - opts.headers.Authorization = `Bearer ${this.credentials.password||""}` - } - } - var payload = null; - - - if (method !== 'GET' && method !== 'HEAD' && typeof msg.payload !== "undefined") { - if (opts.headers['content-type'] == 'multipart/form-data' && typeof msg.payload === "object") { - let formData = new FormData(); - for (var opt in msg.payload) { - if (msg.payload.hasOwnProperty(opt)) { - var val = msg.payload[opt]; - if (val !== undefined && val !== null) { - if (typeof val === 'string' || Buffer.isBuffer(val)) { - formData.append(opt, val); - } else if (typeof val === 'object' && val.hasOwnProperty('value')) { - formData.append(opt,val.value,val.options || {}); - } else { - formData.append(opt,JSON.stringify(val)); - } - } - } - } - // GOT will only set the content-type header with the correct boundary - // if the header isn't set. So we delete it here, for GOT to reset it. - delete opts.headers['content-type']; - opts.body = formData; - } else { - if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) { - payload = msg.payload; - } else if (typeof msg.payload == "number") { - payload = msg.payload+""; - } else { - if (opts.headers['content-type'] == 'application/x-www-form-urlencoded') { - payload = querystring.stringify(msg.payload); - } else { - payload = JSON.stringify(msg.payload); - if (opts.headers['content-type'] == null) { - opts.headers[ctSet] = "application/json"; - } - } - } - if (opts.headers['content-length'] == null) { - if (Buffer.isBuffer(payload)) { - opts.headers[clSet] = payload.length; - } else { - opts.headers[clSet] = Buffer.byteLength(payload); - } - } - opts.body = payload; - } - } - - - if (method == 'GET' && typeof msg.payload !== "undefined" && paytoqs) { - if (typeof msg.payload === "object") { - try { - if (url.indexOf("?") !== -1) { - url += (url.endsWith("?")?"":"&") + querystring.stringify(msg.payload); - } else { - url += "?" + querystring.stringify(msg.payload); - } - } catch(err) { - - node.error(RED._("httpin.errors.invalid-payload"),msg); - nodeDone(); - return; - } - } else { - - node.error(RED._("httpin.errors.invalid-payload"),msg); - nodeDone(); - return; - } - } else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) { - opts.allowGetBody = true; - if (typeof msg.payload === "object") { - opts.body = JSON.stringify(msg.payload); - } else if (typeof msg.payload == "number") { - opts.body = msg.payload+""; - } else if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) { - opts.body = msg.payload; - } - } - - // revert to user supplied Capitalisation if needed. - if (opts.headers.hasOwnProperty('content-type') && (ctSet !== 'content-type')) { - opts.headers[ctSet] = opts.headers['content-type']; - delete opts.headers['content-type']; - } - if (opts.headers.hasOwnProperty('content-length') && (clSet !== 'content-length')) { - opts.headers[clSet] = opts.headers['content-length']; - delete opts.headers['content-length']; - } - - var noproxy; - if (noprox) { - for (var i = 0; i < noprox.length; i += 1) { - if (url.indexOf(noprox[i]) !== -1) { noproxy=true; } - } - } - if (prox && !noproxy) { - var match = prox.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i); - if (match) { - let proxyAgent; - let proxyURL = new URL(prox); - //set username/password to null to stop empty creds header - let proxyOptions = { - proxy: { - protocol: proxyURL.protocol, - hostname: proxyURL.hostname, - port: proxyURL.port, - username: null, - password: null - }, - maxFreeSockets: 256, - maxSockets: 256, - keepAlive: true - } - if (proxyConfig && proxyConfig.credentials) { - let proxyUsername = proxyConfig.credentials.username || ''; - let proxyPassword = proxyConfig.credentials.password || ''; - if (proxyUsername || proxyPassword) { - proxyOptions.proxy.username = proxyUsername; - proxyOptions.proxy.password = proxyPassword; - } - } else if (proxyURL.username || proxyURL.password){ - proxyOptions.proxy.username = proxyURL.username; - proxyOptions.proxy.password = proxyURL.password; - } - //need both incase of http -> https redirect - opts.agent = { - http: new HttpProxyAgent(proxyOptions), - https: new HttpsProxyAgent(proxyOptions) - }; - - } else { - node.warn("Bad proxy url: "+ prox); - } - } - if (tlsNode) { - opts.https = {}; - tlsNode.addTLSOptions(opts.https); - if (opts.https.ca) { - opts.https.certificateAuthority = opts.https.ca; - delete opts.https.ca; - } - if (opts.https.cert) { - opts.https.certificate = opts.https.cert; - delete opts.https.cert; - } - } else { - if (msg.hasOwnProperty('rejectUnauthorized')) { - opts.https = { rejectUnauthorized: msg.rejectUnauthorized }; - } - } - - // Now we have established all of our own headers, take a snapshot - // of their case so we can restore it prior to the request being sent. - if (opts.headers) { - Object.keys(opts.headers).forEach(h => { - originalHeaderMap[h.toLowerCase()] = h - }) - } - got(url,opts).then(res => { - msg.statusCode = res.statusCode; - msg.headers = res.headers; - msg.responseUrl = res.url; - msg.payload = res.body; - msg.redirectList = redirectList; - msg.retry = 0; - - if (msg.headers.hasOwnProperty('set-cookie')) { - msg.responseCookies = extractCookies(msg.headers['set-cookie']); - } - msg.headers['x-node-red-request-node'] = hashSum(msg.headers); - // msg.url = url; // revert when warning above finally removed - if (node.metric()) { - // Calculate request time - var diff = process.hrtime(preRequestTimestamp); - var ms = diff[0] * 1e3 + diff[1] * 1e-6; - var metricRequestDurationMillis = ms.toFixed(3); - node.metric("duration.millis", msg, metricRequestDurationMillis); - if (res.client && res.client.bytesRead) { - node.metric("size.bytes", msg, res.client.bytesRead); - } - if (timingLog) { - emitTimingMetricLog(res.timings, msg); - } - } - - // Convert the payload to the required return type - if (node.ret !== "bin") { - msg.payload = msg.payload.toString('utf8'); // txt - - if (node.ret === "obj") { - try { msg.payload = JSON.parse(msg.payload); } // obj - catch(e) { node.warn(RED._("httpin.errors.json-error")); } - } - } - node.status({}); - nodeSend(msg); - nodeDone(); - }).catch(err => { - // Pre 2.1, any errors would be sent to both Catch node and sent on as normal. - // This is not ideal but is the legacy behaviour of the node. - // 2.1 adds the 'senderr' option, if set to true, will *only* send errors - // to Catch nodes. If false, it still does both behaviours. - // TODO: 3.0 - make it one or the other. - - if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') { - node.error(RED._("common.notification.errors.no-response"), msg); - node.status({fill:"red", shape:"ring", text:"common.notification.errors.no-response"}); - } else { - node.error(err,msg); - node.status({fill:"red", shape:"ring", text:err.code}); - } - msg.payload = err.toString() + " : " + url; - msg.statusCode = err.code || (err.response?err.response.statusCode:undefined); - if (node.metric() && timingLog) { - emitTimingMetricLog(err.timings, msg); - } - if (!sendErrorsToCatch) { - nodeSend(msg); - } - nodeDone(); - }); - }); - - this.on("close",function() { - node.status({}); - }); - - function emitTimingMetricLog(timings, msg) { - const props = [ - "start", - "socket", - "lookup", - "connect", - "secureConnect", - "upload", - "response", - "end", - "error", - "abort" - ]; - if (timings) { - props.forEach(p => { - if (timings[p]) { - node.metric(`timings.${p}`, msg, timings[p]); - } - }); - } - } - - function extractCookies(setCookie) { - var cookies = {}; - setCookie.forEach(function(c) { - var parsedCookie = cookie.parse(c); - var eq_idx = c.indexOf('='); - var key = c.substr(0, eq_idx).trim() - parsedCookie.value = parsedCookie[key]; - delete parsedCookie[key]; - cookies[key] = parsedCookie; - }); - return cookies; - } - } - - RED.nodes.registerType("http request",HTTPRequest,{ - credentials: { - user: {type:"text"}, - password: {type: "password"} - } - }); - - const md5 = (value) => { return crypto.createHash('md5').update(value).digest('hex') } - - function ha1Compute(algorithm, user, realm, pass, nonce, cnonce) { - /** - * RFC 2617: handle both MD5 and MD5-sess algorithms. - * - * If the algorithm directive's value is "MD5" or unspecified, then HA1 is - * HA1=MD5(username:realm:password) - * If the algorithm directive's value is "MD5-sess", then HA1 is - * HA1=MD5(MD5(username:realm:password):nonce:cnonce) - */ - var ha1 = md5(user + ':' + realm + ':' + pass) - if (algorithm && algorithm.toLowerCase() === 'md5-sess') { - return md5(ha1 + ':' + nonce + ':' + cnonce) - } else { - return ha1 - } - } - - - function buildDigestHeader(user, pass, method, path, authHeader) { - var challenge = {} - var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi - for (;;) { - var match = re.exec(authHeader) - if (!match) { - break - } - challenge[match[1]] = match[2] || match[3] - } - var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth' - var nc = qop && '00000001' - var cnonce = qop && uuid().replace(/-/g, '') - var ha1 = ha1Compute(challenge.algorithm, user, challenge.realm, pass, challenge.nonce, cnonce) - var ha2 = md5(method + ':' + path) - var digestResponse = qop - ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2) - : md5(ha1 + ':' + challenge.nonce + ':' + ha2) - var authValues = { - username: user, - realm: challenge.realm, - nonce: challenge.nonce, - uri: path, - qop: qop, - response: digestResponse, - nc: nc, - cnonce: cnonce, - algorithm: challenge.algorithm, - opaque: challenge.opaque - } - - authHeader = [] - for (var k in authValues) { - if (authValues[k]) { - if (k === 'qop' || k === 'nc' || k === 'algorithm') { - authHeader.push(k + '=' + authValues[k]) - } else { - authHeader.push(k + '="' + authValues[k] + '"') - } - } - } - authHeader = 'Digest ' + authHeader.join(', ') - return authHeader - } -} diff --git a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html b/packages/node_modules/@node-red/nodes/core/network/22-websocket.html deleted file mode 100644 index e3ee30eeb..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/22-websocket.js b/packages/node_modules/@node-red/nodes/core/network/22-websocket.js deleted file mode 100644 index 3373c6e72..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/22-websocket.js +++ /dev/null @@ -1,415 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var ws = require("ws"); - var inspect = require("util").inspect; - var url = require("url"); - var HttpsProxyAgent = require('https-proxy-agent'); - - - var serverUpgradeAdded = false; - function handleServerUpgrade(request, socket, head) { - const pathname = url.parse(request.url).pathname; - if (listenerNodes.hasOwnProperty(pathname)) { - listenerNodes[pathname].server.handleUpgrade(request, socket, head, function done(ws) { - listenerNodes[pathname].server.emit('connection', ws, request); - }); - } else { - // Don't destroy the socket as other listeners may want to handle the - // event. - } - } - var listenerNodes = {}; - var activeListenerNodes = 0; - - - // A node red node that sets up a local websocket server - function WebSocketListenerNode(n) { - // Create a RED node - RED.nodes.createNode(this,n); - var node = this; - - // Store local copies of the node configuration (as defined in the .html) - node.path = n.path; - node.wholemsg = (n.wholemsg === "true"); - - node._inputNodes = []; // collection of nodes that want to receive events - node._clients = {}; - // match absolute url - node.isServer = !/^ws{1,2}:\/\//i.test(node.path); - node.closing = false; - node.tls = n.tls; - - if (n.hb) { - var heartbeat = parseInt(n.hb); - if (heartbeat > 0) { - node.heartbeat = heartbeat * 1000; - } - } - - function startconn() { // Connect to remote endpoint - node.tout = null; - var prox, noprox; - if (process.env.http_proxy) { prox = process.env.http_proxy; } - if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } - if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } - if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } - - var noproxy = false; - if (noprox) { - for (var i in noprox) { - if (node.path.indexOf(noprox[i].trim()) !== -1) { noproxy=true; } - } - } - - var agent = undefined; - if (prox && !noproxy) { - agent = new HttpsProxyAgent(prox); - } - - var options = {}; - if (agent) { - options.agent = agent; - } - if (node.tls) { - var tlsNode = RED.nodes.getNode(node.tls); - if (tlsNode) { - tlsNode.addTLSOptions(options); - } - } - var socket = new ws(node.path,options); - socket.setMaxListeners(0); - node.server = socket; // keep for closing - handleConnection(socket); - } - - function handleConnection(/*socket*/socket) { - var id = RED.util.generateId(); - socket.nrId = id; - socket.nrPendingHeartbeat = false; - if (node.isServer) { - node._clients[id] = socket; - node.emit('opened',{count:Object.keys(node._clients).length,id:id}); - } else { - if (node.heartbeat) { - node.heartbeatInterval = setInterval(function() { - if (socket.nrPendingHeartbeat) { - // No pong received - socket.terminate(); - socket.nrErrorHandler(new Error("timeout")); - return; - } - socket.nrPendingHeartbeat = true; - socket.ping(); - },node.heartbeat); - } - } - socket.on('open',function() { - if (!node.isServer) { - node.emit('opened',{count:'',id:id}); - } - }); - socket.on('close',function() { - clearInterval(node.heartbeatInterval); - if (node.isServer) { - delete node._clients[id]; - node.emit('closed',{count:Object.keys(node._clients).length,id:id}); - } else { - node.emit('closed',{count:'',id:id}); - } - if (!node.closing && !node.isServer) { - clearTimeout(node.tout); - node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ? - } - }); - socket.on('message',function(data,flags) { - node.handleEvent(id,socket,'message',data,flags); - }); - socket.nrErrorHandler = function(err) { - clearInterval(node.heartbeatInterval); - node.emit('erro',{err:err,id:id}); - if (!node.closing && !node.isServer) { - clearTimeout(node.tout); - node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ? - } - } - socket.on('error',socket.nrErrorHandler); - socket.on('ping', function() { - socket.nrPendingHeartbeat = false; - }) - socket.on('pong', function() { - socket.nrPendingHeartbeat = false; - }) - } - - if (node.isServer) { - activeListenerNodes++; - if (!serverUpgradeAdded) { - RED.server.on('upgrade', handleServerUpgrade); - serverUpgradeAdded = true - } - - var path = RED.settings.httpNodeRoot || "/"; - path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path); - node.fullPath = path; - - if (listenerNodes.hasOwnProperty(path)) { - node.error(RED._("websocket.errors.duplicate-path",{path: node.path})); - return; - } - listenerNodes[node.fullPath] = node; - var serverOptions = { - noServer: true - } - if (RED.settings.webSocketNodeVerifyClient) { - serverOptions.verifyClient = RED.settings.webSocketNodeVerifyClient; - } - // Create a WebSocket Server - node.server = new ws.Server(serverOptions); - node.server.setMaxListeners(0); - node.server.on('connection', handleConnection); - // Not adding server-initiated heartbeats yet - // node.heartbeatInterval = setInterval(function() { - // node.server.clients.forEach(function(ws) { - // if (ws.nrPendingHeartbeat) { - // // No pong received - // ws.terminate(); - // ws.nrErrorHandler(new Error("timeout")); - // return; - // } - // ws.nrPendingHeartbeat = true; - // ws.ping(); - // }); - // }) - } - else { - node.closing = false; - startconn(); // start outbound connection - } - - node.on("close", function() { - if (node.heartbeatInterval) { - clearInterval(node.heartbeatInterval); - } - if (node.isServer) { - delete listenerNodes[node.fullPath]; - node.server.close(); - node._inputNodes = []; - activeListenerNodes--; - // if (activeListenerNodes === 0 && serverUpgradeAdded) { - // RED.server.removeListener('upgrade', handleServerUpgrade); - // serverUpgradeAdded = false; - // } - } - else { - node.closing = true; - node.server.close(); - if (node.tout) { - clearTimeout(node.tout); - node.tout = null; - } - } - }); - } - RED.nodes.registerType("websocket-listener",WebSocketListenerNode); - RED.nodes.registerType("websocket-client",WebSocketListenerNode); - - WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) { - this._inputNodes.push(handler); - } - - WebSocketListenerNode.prototype.removeInputNode = function(/*Node*/handler) { - this._inputNodes.forEach(function(node, i, inputNodes) { - if (node === handler) { - inputNodes.splice(i, 1); - } - }); - } - - WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags) { - var msg; - if (this.wholemsg) { - try { - msg = JSON.parse(data); - if (typeof msg !== "object" && !Array.isArray(msg) && (msg !== null)) { - msg = { payload:msg }; - } - } - catch(err) { - msg = { payload:data }; - } - } else { - msg = { - payload:data - }; - } - msg._session = {type:"websocket",id:id}; - for (var i = 0; i < this._inputNodes.length; i++) { - this._inputNodes[i].send(msg); - } - } - - WebSocketListenerNode.prototype.broadcast = function(data) { - if (this.isServer) { - for (let client in this._clients) { - if (this._clients.hasOwnProperty(client)) { - try { - this._clients[client].send(data); - } catch(err) { - this.warn(RED._("websocket.errors.send-error")+" "+client+" "+err.toString()) - } - } - } - } - else { - try { - this.server.send(data); - } catch(err) { - this.warn(RED._("websocket.errors.send-error")+" "+err.toString()) - } - } - } - - WebSocketListenerNode.prototype.reply = function(id,data) { - var session = this._clients[id]; - if (session) { - try { - session.send(data); - } - catch(e) { // swallow any errors - } - } - } - - function WebSocketInNode(n) { - RED.nodes.createNode(this,n); - this.server = (n.client)?n.client:n.server; - var node = this; - this.serverConfig = RED.nodes.getNode(this.server); - if (this.serverConfig) { - this.serverConfig.registerInputNode(this); - // TODO: nls - this.serverConfig.on('opened', function(event) { - node.status({ - fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count}), - event:"connect", - _session: {type:"websocket",id:event.id} - }); - }); - this.serverConfig.on('erro', function(event) { - node.status({ - fill:"red",shape:"ring",text:"common.status.error", - event:"error", - _session: {type:"websocket",id:event.id} - }); - }); - this.serverConfig.on('closed', function(event) { - var status; - if (event.count > 0) { - status = {fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count})}; - } else { - status = {fill:"red",shape:"ring",text:"common.status.disconnected"}; - } - status.event = "disconnect"; - status._session = {type:"websocket",id:event.id} - node.status(status); - }); - } else { - this.error(RED._("websocket.errors.missing-conf")); - } - this.on('close', function() { - if (node.serverConfig) { - node.serverConfig.removeInputNode(node); - } - node.status({}); - }); - } - RED.nodes.registerType("websocket in",WebSocketInNode); - - function WebSocketOutNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.server = (n.client)?n.client:n.server; - this.serverConfig = RED.nodes.getNode(this.server); - if (!this.serverConfig) { - return this.error(RED._("websocket.errors.missing-conf")); - } - else { - // TODO: nls - this.serverConfig.on('opened', function(event) { - node.status({ - fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count}), - event:"connect", - _session: {type:"websocket",id:event.id} - }); - }); - this.serverConfig.on('erro', function(event) { - node.status({ - fill:"red",shape:"ring",text:"common.status.error", - event:"error", - _session: {type:"websocket",id:event.id} - }) - }); - this.serverConfig.on('closed', function(event) { - var status; - if (event.count > 0) { - status = {fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count})}; - } else { - status = {fill:"red",shape:"ring",text:"common.status.disconnected"}; - } - status.event = "disconnect"; - status._session = {type:"websocket",id:event.id} - node.status(status); - }); - } - this.on("input", function(msg, nodeSend, nodeDone) { - var payload; - if (this.serverConfig.wholemsg) { - var sess; - if (msg._session) { sess = JSON.stringify(msg._session); } - delete msg._session; - payload = JSON.stringify(msg); - if (sess) { msg._session = JSON.parse(sess); } - } - else if (msg.hasOwnProperty("payload")) { - if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string. - payload = RED.util.ensureString(msg.payload); - } - else { - payload = msg.payload; - } - } - if (payload) { - if (msg._session && msg._session.type == "websocket") { - node.serverConfig.reply(msg._session.id,payload); - } else { - node.serverConfig.broadcast(payload,function(error) { - if (!!error) { - node.warn(RED._("websocket.errors.send-error")+inspect(error)); - } - }); - } - } - nodeDone(); - }); - this.on('close', function() { - node.status({}); - }); - } - RED.nodes.registerType("websocket out",WebSocketOutNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html deleted file mode 100644 index 39e987851..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js deleted file mode 100644 index 2f59227c8..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js +++ /dev/null @@ -1,719 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var reconnectTime = RED.settings.socketReconnectTime||10000; - var socketTimeout = RED.settings.socketTimeout||null; - const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000; - const Denque = require('denque'); - var net = require('net'); - - var connectionPool = {}; - - /** - * Enqueue `item` in `queue` - * @param {Denque} queue - Queue - * @param {*} item - Item to enqueue - * @private - * @returns {Denque} `queue` - */ - const enqueue = (queue, item) => { - // drop msgs from front of queue if size is going to be exceeded - if (queue.length === msgQueueSize) { queue.shift(); } - queue.push(item); - return queue; - }; - - /** - * Shifts item off front of queue - * @param {Deque} queue - Queue - * @private - * @returns {*} Item previously at front of queue - */ - const dequeue = queue => queue.shift(); - - function TcpIn(n) { - RED.nodes.createNode(this,n); - this.host = n.host; - this.port = n.port * 1; - this.topic = n.topic; - this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/ - this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */ - this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r"); - this.base64 = n.base64; - this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server"); - this.closing = false; - this.connected = false; - var node = this; - var count = 0; - - if (!node.server) { - var buffer = null; - var client; - var reconnectTimeout; - var end = false; - var setupTcpClient = function() { - node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); - node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); - var id = RED.util.generateId(); - client = net.connect(node.port, node.host, function() { - buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - node.connected = true; - node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); - }); - client.setKeepAlive(true,120000); - connectionPool[id] = client; - - client.on('data', function (data) { - if (node.datatype != 'buffer') { - data = data.toString(node.datatype); - } - if (node.stream) { - var msg; - if ((node.datatype) === "utf8" && node.newline !== "") { - buffer = buffer+data; - var parts = buffer.split(node.newline); - for (var i = 0; i 0)) { - var msg = {topic:node.topic, payload:buffer}; - msg._session = {type:"tcp",id:id}; - if (buffer.length !== 0) { - end = true; // only ask for fast re-connect if we actually got something - node.send(msg); - } - buffer = null; - } - }); - client.on('close', function() { - delete connectionPool[id]; - node.connected = false; - node.status({fill:"red",shape:"ring",text:"common.status.disconnected",_session:{type:"tcp",id:id}}); - if (!node.closing) { - if (end) { // if we were asked to close then try to reconnect once very quick. - end = false; - reconnectTimeout = setTimeout(setupTcpClient, 20); - } - else { - node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port})); - reconnectTimeout = setTimeout(setupTcpClient, reconnectTime); - } - } else { - if (node.doneClose) { node.doneClose(); } - } - }); - client.on('error', function(err) { - node.log(err); - }); - } - setupTcpClient(); - - this.on('close', function(done) { - node.doneClose = done; - this.closing = true; - if (client) { client.destroy(); } - clearTimeout(reconnectTimeout); - if (!node.connected) { done(); } - }); - } - else { - var server = net.createServer(function (socket) { - socket.setKeepAlive(true,120000); - if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } - var id = RED.util.generateId(); - var fromi; - var fromp; - connectionPool[id] = socket; - count++; - node.status({ - text:RED._("tcpin.status.connections",{count:count}), - event:"connect", - ip:socket.remoteAddress, - port:socket.remotePort, - _session: {type:"tcp",id:id} - }); - - var buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - socket.on('data', function (data) { - if (node.datatype != 'buffer') { - data = data.toString(node.datatype); - } - if (node.stream) { - var msg; - if ((typeof data) === "string" && node.newline !== "") { - buffer = buffer+data; - var parts = buffer.split(node.newline); - for (var i = 0; i 0) { - var msg = {topic:node.topic, payload:buffer, ip:fromi, port:fromp}; - msg._session = {type:"tcp",id:id}; - node.send(msg); - } - buffer = null; - } - }); - socket.on('timeout', function() { - node.log(RED._("tcpin.errors.timeout",{port:node.port})); - socket.end(); - }); - socket.on('close', function() { - delete connectionPool[id]; - count--; - node.status({ - text:RED._("tcpin.status.connections",{count:count}), - event:"disconnect", - ip:socket.remoteAddress, - port:socket.remotePort, - _session: {type:"tcp",id:id} - - }); - }); - socket.on('error',function(err) { - node.log(err); - }); - }); - - server.on('error', function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } - }); - - server.listen(node.port, function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } else { - node.log(RED._("tcpin.status.listening-port",{port:node.port})); - node.on('close', function() { - for (var c in connectionPool) { - if (connectionPool.hasOwnProperty(c)) { - connectionPool[c].end(); - connectionPool[c].unref(); - } - } - node.closing = true; - server.close(); - node.log(RED._("tcpin.status.stopped-listening",{port:node.port})); - }); - } - }); - } - } - RED.nodes.registerType("tcp in",TcpIn); - - - function TcpOut(n) { - RED.nodes.createNode(this,n); - this.host = n.host; - this.port = n.port * 1; - this.base64 = n.base64; - this.doend = n.end || false; - this.beserver = n.beserver; - this.name = n.name; - this.closing = false; - this.connected = false; - var node = this; - - if (!node.beserver||node.beserver=="client") { - var reconnectTimeout; - var client = null; - var end = false; - - var setupTcpClient = function() { - node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); - node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); - client = net.connect(node.port, node.host, function() { - node.connected = true; - node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - client.setKeepAlive(true,120000); - client.on('error', function (err) { - node.log(RED._("tcpin.errors.error",{error:err.toString()})); - }); - client.on('end', function (err) { - node.status({}); - node.connected = false; - }); - client.on('close', function() { - node.status({fill:"red",shape:"ring",text:"common.status.disconnected"}); - node.connected = false; - client.destroy(); - if (!node.closing) { - if (end) { - end = false; - reconnectTimeout = setTimeout(setupTcpClient,20); - } - else { - node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port})); - reconnectTimeout = setTimeout(setupTcpClient,reconnectTime); - } - } else { - if (node.doneClose) { node.doneClose(); } - } - }); - } - setupTcpClient(); - - node.on("input", function(msg, nodeSend, nodeDone) { - if (node.connected && msg.payload != null) { - if (Buffer.isBuffer(msg.payload)) { - client.write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - client.write(Buffer.from(msg.payload,'base64')); - } else { - client.write(Buffer.from(""+msg.payload)); - } - if (node.doend === true) { - end = true; - if (client) { node.status({}); client.destroy(); } - } - } - nodeDone(); - }); - - node.on("close", function(done) { - node.doneClose = done; - this.closing = true; - if (client) { client.destroy(); } - clearTimeout(reconnectTimeout); - if (!node.connected) { done(); } - }); - - } - else if (node.beserver == "reply") { - node.on("input",function(msg, nodeSend, nodeDone) { - if (msg._session && msg._session.type == "tcp") { - var client = connectionPool[msg._session.id]; - if (client) { - if (Buffer.isBuffer(msg.payload)) { - client.write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - client.write(Buffer.from(msg.payload,'base64')); - } else { - client.write(Buffer.from(""+msg.payload)); - } - } - } - else { - for (var i in connectionPool) { - if (Buffer.isBuffer(msg.payload)) { - connectionPool[i].write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - connectionPool[i].write(Buffer.from(msg.payload,'base64')); - } else { - connectionPool[i].write(Buffer.from(""+msg.payload)); - } - } - } - nodeDone(); - }); - } - else { - var connectedSockets = []; - node.status({text:RED._("tcpin.status.connections",{count:0})}); - var server = net.createServer(function (socket) { - socket.setKeepAlive(true,120000); - if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } - node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort})); - socket.on('timeout', function() { - node.log(RED._("tcpin.errors.timeout",{port:node.port})); - socket.end(); - }); - socket.on('data', function(d) { - // console.log("DATA",d) - }); - socket.on('close',function() { - node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort})); - connectedSockets.splice(connectedSockets.indexOf(socket),1); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - socket.on('error',function() { - node.log(RED._("tcpin.errors.socket-error",{host:socket.remoteAddress, port:socket.remotePort})); - connectedSockets.splice(connectedSockets.indexOf(socket),1); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - connectedSockets.push(socket); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - - node.on("input", function(msg, nodeSend, nodeDone) { - if (msg.payload != null) { - var buffer; - if (Buffer.isBuffer(msg.payload)) { - buffer = msg.payload; - } else if (typeof msg.payload === "string" && node.base64) { - buffer = Buffer.from(msg.payload,'base64'); - } else { - buffer = Buffer.from(""+msg.payload); - } - for (var i = 0; i < connectedSockets.length; i += 1) { - if (node.doend === true) { connectedSockets[i].end(buffer); } - else { connectedSockets[i].write(buffer); } - } - } - nodeDone(); - }); - - server.on('error', function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } - }); - - server.listen(node.port, function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } else { - node.log(RED._("tcpin.status.listening-port",{port:node.port})); - node.on('close', function() { - for (var c in connectedSockets) { - if (connectedSockets.hasOwnProperty(c)) { - connectedSockets[c].end(); - connectedSockets[c].unref(); - } - } - server.close(); - node.log(RED._("tcpin.status.stopped-listening",{port:node.port})); - }); - } - }); - } - } - RED.nodes.registerType("tcp out",TcpOut); - - - function TcpGet(n) { - RED.nodes.createNode(this,n); - this.server = n.server; - this.port = Number(n.port); - this.out = n.out; - this.ret = n.ret || "buffer"; - this.splitc = n.splitc; - - if (this.out === "immed") { this.splitc = -1; this.out = "time"; } - if (this.out !== "char") { this.splitc = Number(this.splitc); } - else { - if (this.splitc[0] == '\\') { - this.splitc = parseInt(this.splitc.replace("\\n",0x0A).replace("\\r",0x0D).replace("\\t",0x09).replace("\\e",0x1B).replace("\\f",0x0C).replace("\\0",0x00)); - } // jshint ignore:line - if (typeof this.splitc == "string") { - if (this.splitc.substr(0,2) == "0x") { - this.splitc = parseInt(this.splitc); - } - else { - this.splitc = this.splitc.charCodeAt(0); - } - } // jshint ignore:line - } - - var node = this; - - var clients = {}; - - this.on("input", function(msg, nodeSend, nodeDone) { - var i = 0; - if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) { - msg.payload = msg.payload.toString(); - } - - var host = node.server || msg.host; - var port = node.port || msg.port; - - // Store client information independently - // the clients object will have: - // clients[id].client, clients[id].msg, clients[id].timeout - var connection_id = host + ":" + port; - if (connection_id !== node.last_id) { - node.status({}); - node.last_id = connection_id; - } - clients[connection_id] = clients[connection_id] || { - msgQueue: new Denque(), - connected: false, - connecting: false - }; - enqueue(clients[connection_id].msgQueue, {msg:msg, nodeSend:nodeSend, nodeDone:nodeDone}); - clients[connection_id].lastMsg = msg; - - if (!clients[connection_id].connecting && !clients[connection_id].connected) { - var buf; - if (this.out == "count") { - if (this.splitc === 0) { buf = Buffer.alloc(1); } - else { buf = Buffer.alloc(this.splitc); } - } - else { buf = Buffer.alloc(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully - - clients[connection_id].client = net.Socket(); - if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);} - - if (host && port) { - clients[connection_id].connecting = true; - clients[connection_id].client.connect(port, host, function() { - //node.log(RED._("tcpin.errors.client-connected")); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].connected = true; - clients[connection_id].connecting = false; - let event; - while (event = dequeue(clients[connection_id].msgQueue)) { - clients[connection_id].client.write(event.msg.payload); - event.nodeDone(); - } - if (node.out === "time" && node.splitc < 0) { - clients[connection_id].connected = clients[connection_id].connecting = false; - clients[connection_id].client.end(); - delete clients[connection_id]; - node.status({}); - } - } - }); - } - else { - node.warn(RED._("tcpin.errors.no-host")); - } - - clients[connection_id].client.on('data', function(data) { - if (node.out === "sit") { // if we are staying connected just send the buffer - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = RED.util.cloneMessage(data); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - } - } - // else if (node.splitc === 0) { - // clients[connection_id].msg.payload = data; - // node.send(clients[connection_id].msg); - // } - else { - for (var j = 0; j < data.length; j++ ) { - if (node.out === "time") { - if (clients[connection_id]) { - // do the timer thing - if (clients[connection_id].timeout) { - i += 1; - buf[i] = data[j]; - } - else { - clients[connection_id].timeout = setTimeout(function () { - if (clients[connection_id]) { - clients[connection_id].timeout = null; - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i+1); - buf.copy(msg.payload,0,0,i+1); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - } - }, node.splitc); - i = 0; - buf[0] = data[j]; - } - } - } - // count bytes into a buffer... - else if (node.out == "count") { - buf[i] = data[j]; - i += 1; - if ( i >= node.splitc) { - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i); - buf.copy(msg.payload,0,0,i); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - i = 0; - } - } - } - // look for a char - else { - buf[i] = data[j]; - i += 1; - if (data[j] == node.splitc) { - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i); - buf.copy(msg.payload,0,0,i); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - i = 0; - } - } - } - } - } - }); - - clients[connection_id].client.on('end', function() { - //console.log("END"); - node.status({fill:"grey",shape:"ring",text:"common.status.disconnected"}); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].connected = clients[connection_id].connecting = false; - clients[connection_id].client = null; - } - }); - - clients[connection_id].client.on('close', function() { - //console.log("CLOSE"); - if (clients[connection_id]) { - clients[connection_id].connected = clients[connection_id].connecting = false; - } - - var anyConnected = false; - - for (var client in clients) { - if (clients[client].connected) { - anyConnected = true; - break; - } - } - if (node.doneClose && !anyConnected) { - clients = {}; - node.doneClose(); - } - }); - - clients[connection_id].client.on('error', function() { - //console.log("ERROR"); - node.status({fill:"red",shape:"ring",text:"common.status.error"}); - node.error(RED._("tcpin.errors.connect-fail") + " " + connection_id, msg); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - }); - - clients[connection_id].client.on('timeout',function() { - //console.log("TIMEOUT"); - if (clients[connection_id]) { - clients[connection_id].connected = clients[connection_id].connecting = false; - node.status({fill:"grey",shape:"dot",text:"tcpin.errors.connect-timeout"}); - //node.warn(RED._("tcpin.errors.connect-timeout")); - if (clients[connection_id].client) { - clients[connection_id].connecting = true; - clients[connection_id].client.connect(port, host, function() { - clients[connection_id].connected = true; - clients[connection_id].connecting = false; - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - } - } - }); - } - else if (!clients[connection_id].connecting && clients[connection_id].connected) { - if (clients[connection_id] && clients[connection_id].client) { - let event = dequeue(clients[connection_id].msgQueue) - clients[connection_id].client.write(event.msg.payload); - event.nodeDone(); - } - } - }); - - this.on("close", function(done) { - node.doneClose = done; - for (var cl in clients) { - if (clients[cl].hasOwnProperty("client")) { - clients[cl].client.destroy(); - } - } - node.status({}); - - // this is probably not necessary and may be removed - var anyConnected = false; - for (var c in clients) { - if (clients[c].connected) { - anyConnected = true; - break; - } - } - if (!anyConnected) { clients = {}; } - done(); - }); - - } - RED.nodes.registerType("tcp request",TcpGet); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/32-udp.html b/packages/node_modules/@node-red/nodes/core/network/32-udp.html deleted file mode 100644 index 3a4e241af..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/32-udp.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/32-udp.js b/packages/node_modules/@node-red/nodes/core/network/32-udp.js deleted file mode 100644 index d42d3a7c3..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/32-udp.js +++ /dev/null @@ -1,280 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var os = require('os'); - var dgram = require('dgram'); - var udpInputPortsInUse = {}; - - // The Input Node - function UDPin(n) { - RED.nodes.createNode(this,n); - this.group = n.group; - this.port = n.port; - this.datatype = n.datatype; - this.iface = n.iface || null; - this.multicast = n.multicast; - this.ipv = n.ipv || "udp4"; - var node = this; - - if (node.iface && node.iface.indexOf(".") === -1) { - try { - if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } - } - else { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } - } - } - catch(e) { - node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface})); - node.iface = null; - } - } - - var opts = {type:node.ipv, reuseAddr:true}; - if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; } - var server; - - if (!udpInputPortsInUse.hasOwnProperty(node.port)) { - server = dgram.createSocket(opts); // default to udp4 - server.bind(node.port, function() { - if (node.multicast == "true") { - server.setBroadcast(true); - server.setMulticastLoopback(false); - try { - server.setMulticastTTL(128); - server.addMembership(node.group,node.iface); - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - node.log(RED._("udp.status.mc-group",{group:node.group})); - } catch (e) { - if (e.errno == "EINVAL") { - node.error(RED._("udp.errors.bad-mcaddress")); - } else if (e.errno == "ENODEV") { - node.error(RED._("udp.errors.interface")); - } else { - node.error(RED._("udp.errors.error",{error:e.errno})); - } - } - } - }); - udpInputPortsInUse[node.port] = server; - } - else { - node.log(RED._("udp.errors.alreadyused",{port:node.port})); - server = udpInputPortsInUse[node.port]; // re-use existing - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - } - - server.on("error", function (err) { - if ((err.code == "EACCES") && (node.port < 1024)) { - node.error(RED._("udp.errors.access-error")); - } else { - node.error(RED._("udp.errors.error",{error:err.code})); - } - server.close(); - }); - - server.on('message', function (message, remote) { - var msg; - if (node.datatype =="base64") { - msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; - } else if (node.datatype =="utf8") { - msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; - } else { - msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; - } - node.send(msg); - }); - - server.on('listening', function () { - var address = server.address(); - node.log(RED._("udp.status.listener-at",{host:node.iface||address.address,port:address.port})); - - }); - - node.on("close", function() { - try { - if (node.multicast == "true") { server.dropMembership(node.group); } - server.close(); - node.log(RED._("udp.status.listener-stopped")); - } catch (err) { - //node.error(err); - } - if (udpInputPortsInUse.hasOwnProperty(node.port)) { - delete udpInputPortsInUse[node.port]; - } - node.status({}); - }); - - } - RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-ports.read'), function(req,res) { - res.json(Object.keys(udpInputPortsInUse)); - }); - RED.nodes.registerType("udp in",UDPin); - - - - // The Output Node - function UDPout(n) { - RED.nodes.createNode(this,n); - //this.group = n.group; - this.port = n.port; - this.outport = n.outport||""; - this.base64 = n.base64; - this.addr = n.addr; - this.iface = n.iface || null; - this.multicast = n.multicast; - this.ipv = n.ipv || "udp4"; - var node = this; - - if (node.iface && node.iface.indexOf(".") === -1) { - try { - if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } - } - else { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } - } - } - catch(e) { - node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface})); - node.iface = null; - } - } - - var opts = {type:node.ipv, reuseAddr:true}; - - var sock; - var p = this.outport || this.port || "0"; - node.tout = setTimeout(function() { - if ((p != 0) && udpInputPortsInUse[p]) { - sock = udpInputPortsInUse[p]; - if (node.multicast != "false") { - sock.setBroadcast(true); - sock.setMulticastLoopback(false); - } - node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port})); - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - } - else { - sock = dgram.createSocket(opts); // default to udp4 - if (node.multicast != "false") { - sock.bind(node.outport, function() { // have to bind before you can enable broadcast... - sock.setBroadcast(true); // turn on broadcast - sock.setMulticastLoopback(false); // turn off loopback - if (node.multicast == "multi") { - try { - sock.setMulticastTTL(128); - sock.addMembership(node.addr,node.iface); // Add to the multicast group - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - node.log(RED._("udp.status.mc-ready",{iface:node.iface,outport:node.outport,host:node.addr,port:node.port})); - } catch (e) { - if (e.errno == "EINVAL") { - node.error(RED._("udp.errors.bad-mcaddress")); - } else if (e.errno == "ENODEV") { - node.error(RED._("udp.errors.interface")); - } else { - node.error(RED._("udp.errors.error",{error:e.errno})); - } - } - } else { - node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port})); - } - }); - } else if ((node.outport !== "") && (!udpInputPortsInUse[node.outport])) { - sock.bind(node.outport); - node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port})); - } else { - node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port})); - } - sock.on("error", function(err) { - // Any async error will also get reported in the sock.send call. - // This handler is needed to ensure the error marked as handled to - // prevent it going to the global error handler and shutting node-red - // down. - }); - udpInputPortsInUse[p] = sock; - } - - node.on("input", function(msg, nodeSend, nodeDone) { - if (msg.hasOwnProperty("payload")) { - var add = node.addr || msg.ip || ""; - var por = node.port || msg.port || 0; - if (add === "") { - node.warn(RED._("udp.errors.ip-notset")); - nodeDone(); - } else if (por === 0) { - node.warn(RED._("udp.errors.port-notset")); - nodeDone(); - } else if (isNaN(por) || (por < 1) || (por > 65535)) { - node.warn(RED._("udp.errors.port-invalid")); - nodeDone(); - } else { - var message; - if (node.base64) { - message = Buffer.from(msg.payload, 'base64'); - } else if (msg.payload instanceof Buffer) { - message = msg.payload; - } else { - message = Buffer.from(""+msg.payload); - } - sock.send(message, 0, message.length, por, add, function(err, bytes) { - if (err) { - node.error("udp : "+err,msg); - } - message = null; - nodeDone(); - }); - } - } - }); - }, 75); - - node.on("close", function() { - if (node.tout) { clearTimeout(node.tout); } - try { - if (node.multicast == "multi") { sock.dropMembership(node.group); } - sock.close(); - node.log(RED._("udp.status.output-stopped")); - } catch (err) { - //node.error(err); - } - if (udpInputPortsInUse.hasOwnProperty(p)) { - delete udpInputPortsInUse[p]; - } - node.status({}); - }); - } - RED.nodes.registerType("udp out",UDPout); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js b/packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js deleted file mode 100644 index c472f01e9..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var util = require("util"); -var mqtt = require("mqtt"); -var events = require("events"); - -util.log("[warn] nodes/core/io/lib/mqtt.js is deprecated and will be removed in a future release of Node-RED. Please report this usage to the Node-RED mailing list."); - -//var inspect = require("util").inspect; - -//var Client = module.exports.Client = function( - -var port = 1883; -var host = "localhost"; - -function MQTTClient(port,host) { - this.port = port||1883; - this.host = host||"localhost"; - this.messageId = 1; - this.pendingSubscriptions = {}; - this.inboundMessages = {}; - this.lastOutbound = (new Date()).getTime(); - this.lastInbound = (new Date()).getTime(); - this.connected = false; - - this._nextMessageId = function() { - this.messageId += 1; - if (this.messageId > 0xFFFF) { - this.messageId = 1; - } - return this.messageId; - } - events.EventEmitter.call(this); -} -util.inherits(MQTTClient, events.EventEmitter); - -MQTTClient.prototype.connect = function(options) { - if (!this.connected) { - var self = this; - options = options||{}; - self.options = options; - self.options.keepalive = options.keepalive||15; - self.options.clean = self.options.clean||true; - self.options.protocolId = 'MQIsdp'; - self.options.protocolVersion = 3; - - self.client = mqtt.createConnection(this.port,this.host,function(err,client) { - if (err) { - self.connected = false; - clearInterval(self.watchdog); - self.connectionError = true; - //util.log('[mqtt] ['+self.uid+'] connection error 1 : '+inspect(err)); - self.emit('connectionlost',err); - return; - } - client.on('close',function(e) { - //util.log('[mqtt] ['+self.uid+'] on close'); - clearInterval(self.watchdog); - if (!self.connectionError) { - if (self.connected) { - self.connected = false; - self.emit('connectionlost',e); - } else { - self.emit('disconnect'); - } - } - }); - client.on('error',function(e) { - //util.log('[mqtt] ['+self.uid+'] on error : '+inspect(e)); - clearInterval(self.watchdog); - if (self.connected) { - self.connected = false; - self.emit('connectionlost',e); - } - }); - client.on('connack',function(packet) { - if (packet.returnCode === 0) { - self.watchdog = setInterval(function(self) { - var now = (new Date()).getTime(); - - //util.log('[mqtt] ['+self.uid+'] watchdog '+inspect({connected:self.connected,connectionError:self.connectionError,pingOutstanding:self.pingOutstanding,now:now,lastOutbound:self.lastOutbound,lastInbound:self.lastInbound})); - - if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) { - if (self.pingOutstanding) { - //util.log('[mqtt] ['+self.uid+'] watchdog pingOustanding - disconnect'); - try { - self.client.disconnect(); - } catch (err) { - } - } else { - //util.log('[mqtt] ['+self.uid+'] watchdog pinging'); - self.lastOutbound = (new Date()).getTime(); - self.lastInbound = (new Date()).getTime(); - self.pingOutstanding = true; - self.client.pingreq(); - } - } - - },self.options.keepalive*500,self); - self.pingOutstanding = false; - self.lastInbound = (new Date()).getTime() - self.lastOutbound = (new Date()).getTime() - self.connected = true; - self.connectionError = false; - self.emit('connect'); - } else { - self.connected = false; - self.emit('connectionlost'); - } - }); - client.on('suback',function(packet) { - self.lastInbound = (new Date()).getTime() - var topic = self.pendingSubscriptions[packet.messageId]; - self.emit('subscribe',topic,packet.granted[0]); - delete self.pendingSubscriptions[packet.messageId]; - }); - client.on('unsuback',function(packet) { - self.lastInbound = (new Date()).getTime() - var topic = self.pendingSubscriptions[packet.messageId]; - self.emit('unsubscribe',topic); - delete self.pendingSubscriptions[packet.messageId]; - }); - client.on('publish',function(packet) { - self.lastInbound = (new Date()).getTime(); - if (packet.qos < 2) { - var p = packet; - self.emit('message',p.topic,p.payload,p.qos,p.retain); - } else { - self.inboundMessages[packet.messageId] = packet; - this.lastOutbound = (new Date()).getTime() - self.client.pubrec(packet); - } - if (packet.qos == 1) { - this.lastOutbound = (new Date()).getTime() - self.client.puback(packet); - } - }); - - client.on('pubrel',function(packet) { - self.lastInbound = (new Date()).getTime() - var p = self.inboundMessages[packet.messageId]; - if (p) { - self.emit('message',p.topic,p.payload,p.qos,p.retain); - delete self.inboundMessages[packet.messageId]; - } - self.lastOutbound = (new Date()).getTime() - self.client.pubcomp(packet); - }); - - client.on('puback',function(packet) { - self.lastInbound = (new Date()).getTime() - // outbound qos-1 complete - }); - - client.on('pubrec',function(packet) { - self.lastInbound = (new Date()).getTime() - self.lastOutbound = (new Date()).getTime() - self.client.pubrel(packet); - }); - client.on('pubcomp',function(packet) { - self.lastInbound = (new Date()).getTime() - // outbound qos-2 complete - }); - client.on('pingresp',function(packet) { - //util.log('[mqtt] ['+self.uid+'] received pingresp'); - self.lastInbound = (new Date()).getTime() - self.pingOutstanding = false; - }); - - this.lastOutbound = (new Date()).getTime() - this.connectionError = false; - client.connect(self.options); - }); - } -} - -MQTTClient.prototype.subscribe = function(topic,qos) { - var self = this; - if (self.connected) { - var options = { - subscriptions:[{topic:topic,qos:qos}], - messageId: self._nextMessageId() - }; - this.pendingSubscriptions[options.messageId] = topic; - this.lastOutbound = (new Date()).getTime(); - self.client.subscribe(options); - self.client.setPacketEncoding('binary'); - } -} -MQTTClient.prototype.unsubscribe = function(topic) { - var self = this; - if (self.connected) { - var options = { - unsubscriptions:[topic], - messageId: self._nextMessageId() - }; - this.pendingSubscriptions[options.messageId] = topic; - this.lastOutbound = (new Date()).getTime() - self.client.unsubscribe(options); - } -} - -MQTTClient.prototype.publish = function(topic,payload,qos,retain) { - var self = this; - if (self.connected) { - - if (!Buffer.isBuffer(payload)) { - if (typeof payload === "object") { - payload = JSON.stringify(payload); - } else if (typeof payload !== "string") { - payload = ""+payload; - } - } - var options = { - topic: topic, - payload: payload, - qos: qos||0, - retain:retain||false - }; - if (options.qos !== 0) { - options.messageId = self._nextMessageId(); - } - this.lastOutbound = (new Date()).getTime() - self.client.publish(options); - } -} - -MQTTClient.prototype.disconnect = function() { - var self = this; - if (this.connected) { - this.connected = false; - try { - this.client.disconnect(); - } catch(err) { - } - } -} -MQTTClient.prototype.isConnected = function() { - return this.connected; -} -module.exports.createClient = function(port,host) { - var mqtt_client = new MQTTClient(port,host); - return mqtt_client; -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html deleted file mode 100644 index 80778ac1b..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js deleted file mode 100644 index 184f40bdd..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - function CSVNode(n) { - RED.nodes.createNode(this,n); - this.template = (n.temp || ""); - this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r"); - this.quo = '"'; - this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r"); - this.winflag = (this.ret === "\r\n"); - this.lineend = "\n"; - this.multi = n.multi || "one"; - this.hdrin = n.hdrin || false; - this.hdrout = n.hdrout || "none"; - this.goodtmpl = true; - this.skip = parseInt(n.skip || 0); - this.store = []; - this.parsestrings = n.strings; - this.include_empty_strings = n.include_empty_strings || false; - this.include_null_values = n.include_null_values || false; - if (this.parsestrings === undefined) { this.parsestrings = true; } - if (this.hdrout === false) { this.hdrout = "none"; } - if (this.hdrout === true) { this.hdrout = "all"; } - var tmpwarn = true; - var node = this; - var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); - - // pass in an array of column names to be trimmed, de-quoted and retrimmed - var clean = function(col,sep) { - if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); } - col = col.trim().split(re) || [""]; - col = col.map(x => x.replace(/"/g,'').trim()); - if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; } - else { node.goodtmpl = true; } - return col; - } - var template = clean(node.template,','); - var notemplate = template.length === 1 && template[0] === ''; - node.hdrSent = false; - - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - node.hdrSent = false; - } - if (msg.hasOwnProperty("payload")) { - if (typeof msg.payload == "object") { // convert object to CSV string - try { - if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) { - template = clean(node.template); - } - var ou = ""; - if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; } - if (node.hdrout !== "none" && node.hdrSent === false) { - if ((template.length === 1) && (template[0] === '')) { - if (msg.hasOwnProperty("columns")) { - template = clean(msg.columns || "",","); - } - else { - template = Object.keys(msg.payload[0]); - } - } - ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret; - if (node.hdrout === "once") { node.hdrSent = true; } - } - for (var s = 0; s < msg.payload.length; s++) { - if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) { - if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; } - for (var t = 0; t < msg.payload[s].length; t++) { - if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; } - if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes - msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""'); - msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo; - } - else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas" - msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo; - } - } - ou += msg.payload[s].join(node.sep) + node.ret; - } - else { - if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) { - template = clean(msg.columns || "",","); - } - if ((template.length === 1) && (template[0] === '')) { - /* istanbul ignore else */ - if (tmpwarn === true) { // just warn about missing template once - node.warn(RED._("csv.errors.obj_csv")); - tmpwarn = false; - } - for (var p in msg.payload[0]) { - /* istanbul ignore else */ - if (msg.payload[s].hasOwnProperty(p)) { - /* istanbul ignore else */ - if (typeof msg.payload[s][p] !== "object") { - var q = "" + msg.payload[s][p]; - if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes - q = q.replace(/"/g, '""'); - ou += node.quo + q + node.quo + node.sep; - } - else if (q.indexOf(node.sep) !== -1) { // add quotes if any "commas" - ou += node.quo + q + node.quo + node.sep; - } - else { ou += q + node.sep; } // otherwise just add - } - } - } - ou = ou.slice(0,-1) + node.ret; - } - else { - for (var t=0; t < template.length; t++) { - if (template[t] === '') { - ou += node.sep; - } - else { - var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']")); - /* istanbul ignore else */ - if (p === "undefined") { p = ""; } - if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes - p = p.replace(/"/g, '""'); - ou += node.quo + p + node.quo + node.sep; - } - else if (p.indexOf(node.sep) !== -1) { // add quotes if any "commas" - ou += node.quo + p + node.quo + node.sep; - } - else { ou += p + node.sep; } // otherwise just add - } - } - ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline" - } - } - } - msg.payload = ou; - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(','); - if (msg.payload !== '') { send(msg); } - done(); - } - catch(e) { done(e); } - } - else if (typeof msg.payload == "string") { // convert CSV string to object - try { - var f = true; // flag to indicate if inside or outside a pair of quotes true = outside. - var j = 0; // pointer into array of template items - var k = [""]; // array of data for each of the template items - var o = {}; // output object to build up - var a = []; // output array is needed for multiline option - var first = true; // is this the first line - var last = false; - var line = msg.payload; - var linecount = 0; - var tmp = ""; - var has_parts = msg.hasOwnProperty("parts"); - var reg = /^[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+$/i; - if (msg.hasOwnProperty("parts")) { - linecount = msg.parts.index; - if (msg.parts.index > node.skip) { first = false; } - if (msg.parts.hasOwnProperty("count") && (msg.parts.index+1 >= msg.parts.count)) { last = true; } - } - - // For now we are just going to assume that any \r or \n means an end of line... - // got to be a weird csv that has singleton \r \n in it for another reason... - - // Now process the whole file/line - var nocr = (line.match(/[\r\n]/g)||[]).length; - if (has_parts && node.multi === "mult" && nocr > 1) { tmp = ""; first = true; } - for (var i = 0; i < line.length; i++) { - if (first && (linecount < node.skip)) { - if (line[i] === "\n") { linecount += 1; } - continue; - } - if ((node.hdrin === true) && first) { // if the template is in the first line - if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break - if (line.length - i === 1) { tmp += line[i]; } - template = clean(tmp,node.sep); - first = false; - } - else { tmp += line[i]; } - } - else { - if (line[i] === node.quo) { // if it's a quote toggle inside or outside - f = !f; - if (line[i-1] === node.quo) { - if (f === false) { k[j] += '\"'; } - } // if it's a quotequote then it's actually a quote - //if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; } - } - else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish - if (!node.goodtmpl) { template[j] = "col"+(j+1); } - if ( template[j] && (template[j] !== "") ) { - // if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null - if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null; - if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); } - if (node.include_null_values && k[j] === null) o[template[j]] = k[j]; - if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j]; - if (k[j] !== null && k[j] !== "") o[template[j]] = k[j]; - } - j += 1; - // if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",' - k[j] = line.length - 1 === i ? null : ""; - } - else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines - //console.log(j,k,o,k[j]); - if (!node.goodtmpl) { template[j] = "col"+(j+1); } - if ( template[j] && (template[j] !== "") ) { - // if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3' - if (line[i-1] === node.sep) k[j] = null; - if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); } - else { if (k[j] !== null) k[j].replace(/\r$/,''); } - if (node.include_null_values && k[j] === null) o[template[j]] = k[j]; - if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j]; - if (k[j] !== null && k[j] !== "") o[template[j]] = k[j]; - } - if (JSON.stringify(o) !== "{}") { // don't send empty objects - a.push(o); // add to the array - } - j = 0; - k = [""]; - o = {}; - f = true; // reset in/out flag ready for next line. - } - else { // just add to the part of the message - k[j] += line[i]; - } - } - } - // Finished so finalize and send anything left - if (f === false) { node.warn(RED._("csv.errors.bad_csv")); } - if (!node.goodtmpl) { template[j] = "col"+(j+1); } - - if ( template[j] && (template[j] !== "") ) { - if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); } - else { if (k[j] !== null) k[j].replace(/\r$/,''); } - if (node.include_null_values && k[j] === null) o[template[j]] = k[j]; - if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j]; - if (k[j] !== null && k[j] !== "") o[template[j]] = k[j]; - } - - if (JSON.stringify(o) !== "{}") { // don't send empty objects - a.push(o); // add to the array - } - - if (node.multi !== "one") { - msg.payload = a; - if (has_parts && nocr <= 1) { - if (JSON.stringify(o) !== "{}") { - node.store.push(o); - } - if (msg.parts.index + 1 === msg.parts.count) { - msg.payload = node.store; - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); - delete msg.parts; - send(msg); - node.store = []; - } - } - else { - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); - send(msg); // finally send the array - } - } - else { - var len = a.length; - for (var i = 0; i < len; i++) { - var newMessage = RED.util.cloneMessage(msg); - newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); - newMessage.payload = a[i]; - if (!has_parts) { - newMessage.parts = { - id: msg._msgid, - index: i, - count: len - }; - } - else { - newMessage.parts.index -= node.skip; - newMessage.parts.count -= node.skip; - if (node.hdrin) { // if we removed the header line then shift the counts by 1 - newMessage.parts.index -= 1; - newMessage.parts.count -= 1; - } - } - if (last) { newMessage.complete = true; } - send(newMessage); - } - if (has_parts && last && len === 0) { - send({complete:true}); - } - } - node.linecount = 0; - done(); - } - catch(e) { done(e); } - } - else { node.warn(RED._("csv.errors.csv_js")); done(); } - } - else { - if (!msg.hasOwnProperty("reset")) { - node.send(msg); // If no payload and not reset - just pass it on. - } - done(); - } - }); - } - RED.nodes.registerType("csv",CSVNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html b/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html deleted file mode 100644 index 4509dd054..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js deleted file mode 100644 index 073b98689..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var cheerio = require('cheerio'); - - function CheerioNode(n) { - RED.nodes.createNode(this,n); - this.property = n.property||"payload"; - this.outproperty = n.outproperty||this.property||"payload"; - this.tag = n.tag; - this.ret = n.ret || "html"; - this.as = n.as || "single"; - var node = this; - this.on("input", function(msg,send,done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var tag = node.tag; - if (msg.hasOwnProperty("select")) { tag = node.tag || msg.select; } - try { - var $ = cheerio.load(value); - var pay = []; - var count = 0; - $(tag).each(function() { - count++; - }); - var index = 0; - $(tag).each(function() { - if (node.as === "multi") { - var pay2 = null; - if (node.ret === "html") { pay2 = cheerio.load($(this).html().trim(),null,false).xml(); } - if (node.ret === "text") { pay2 = $(this).text(); } - if (node.ret === "attr") { - pay2 = Object.assign({},this.attribs); - } - //if (node.ret === "val") { pay2 = $(this).val(); } - /* istanbul ignore else */ - if (pay2) { - var new_msg = RED.util.cloneMessage(msg); - RED.util.setMessageProperty(new_msg,node.outproperty,pay2); - new_msg.parts = { - id: msg._msgid, - index: index, - count: count, - type: "string", - ch: "" - }; - send(new_msg); - } - } - if (node.as === "single") { - if (node.ret === "html") { pay.push( cheerio.load($(this).html().trim(),null,false).xml() ); } - if (node.ret === "text") { pay.push( $(this).text() ); } - if (node.ret === "attr") { - var attribs = Object.assign({},this.attribs); - pay.push( attribs ); - } - //if (node.ret === "val") { pay.push( $(this).val() ); } - } - index++; - }); - if (node.as === "single") { // Always return an array - even if blank - RED.util.setMessageProperty(msg,node.outproperty,pay); - send(msg); - } - done(); - } - catch (error) { - done(error.message); - } - } - else { send(msg); done(); } // If no payload - just pass it on. - }); - } - RED.nodes.registerType("html",CheerioNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html b/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html deleted file mode 100644 index 6599cf0e0..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js b/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js deleted file mode 100644 index a68edc681..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - const Ajv = require('ajv'); - const ajv = new Ajv({allErrors: true}); - ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); - - function JSONNode(n) { - RED.nodes.createNode(this,n); - this.indent = n.pretty ? 4 : 0; - this.action = n.action||""; - this.property = n.property||"payload"; - this.schema = null; - this.compiledSchema = null; - - var node = this; - - this.on("input", function(msg,send,done) { - var validate = false; - if (msg.schema) { - // If input schema is different, re-compile it - if (JSON.stringify(this.schema) != JSON.stringify(msg.schema)) { - try { - this.compiledSchema = ajv.compile(msg.schema); - this.schema = msg.schema; - } catch(e) { - this.schema = null; - this.compiledSchema = null; - done(RED._("json.errors.schema-error-compile")); - return; - } - } - validate = true; - } - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - if (typeof value === "string") { - if (node.action === "" || node.action === "obj") { - try { - RED.util.setMessageProperty(msg,node.property,JSON.parse(value)); - if (validate) { - if (this.compiledSchema(msg[node.property])) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - catch(e) { done(e.message); } - } else { - // If node.action is str and value is str - if (validate) { - if (this.compiledSchema(JSON.parse(msg[node.property]))) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - } - else if ((typeof value === "object") || (typeof value === "boolean") || (typeof value === "number")) { - if (node.action === "" || node.action === "str") { - if (!Buffer.isBuffer(value)) { - try { - if (validate) { - if (this.compiledSchema(value)) { - RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); - send(msg); - done(); - } - } - catch(e) { done(RED._("json.errors.dropped-error")); } - } - else { node.warn(RED._("json.errors.dropped-object")); done(); } - } else { - // If node.action is obj and value is object - if (validate) { - if (this.compiledSchema(value)) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - } - else { node.warn(RED._("json.errors.dropped")); done(); } - } - else { send(msg); done(); } // If no property - just pass it on. - }); - } - RED.nodes.registerType("json",JSONNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.html b/packages/node_modules/@node-red/nodes/core/parsers/70-XML.html deleted file mode 100644 index 642fe4d04..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js deleted file mode 100644 index a778c4d72..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js +++ /dev/null @@ -1,49 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - var xml2js = require('xml2js'); - var parseString = xml2js.parseString; - - function XMLNode(n) { - RED.nodes.createNode(this,n); - this.attrkey = n.attr; - this.charkey = n.chr; - this.property = n.property||"payload"; - var node = this; - this.on("input", function(msg,send,done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var options; - if (typeof value === "object") { - options = {renderOpts:{pretty:false}}; - if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; } - options.async = false; - var builder = new xml2js.Builder(options); - value = builder.buildObject(value, options); - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - else if (typeof value == "string") { - options = {}; - if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; } - options.async = true; - options.attrkey = node.attrkey || options.attrkey || '$'; - options.charkey = node.charkey || options.charkey || '_'; - parseString(value, options, function (err, result) { - if (err) { done(err); } - else { - value = result; - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - }); - } - else { node.warn(RED._("xml.errors.xml_js")); done(); } - } - else { send(msg); done(); } // If no property - just pass it on. - }); - } - RED.nodes.registerType("xml",XMLNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html b/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html deleted file mode 100644 index deaf47d95..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js deleted file mode 100644 index 69a6bf444..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js +++ /dev/null @@ -1,41 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - var yaml = require('js-yaml'); - function YAMLNode(n) { - RED.nodes.createNode(this,n); - this.property = n.property||"payload"; - var node = this; - this.on("input", function(msg,send,done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - if (typeof value === "string") { - try { - value = yaml.load(value); - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - catch(e) { done(e.message); } - } - else if (typeof value === "object") { - if (!Buffer.isBuffer(value)) { - try { - value = yaml.dump(value); - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - catch(e) { - done(RED._("yaml.errors.dropped-error")); - } - } - else { node.warn(RED._("yaml.errors.dropped-object")); done(); } - } - else { node.warn(RED._("yaml.errors.dropped")); done(); } - } - else { send(msg); done(); } // If no payload - just pass it on. - }); - } - RED.nodes.registerType("yaml",YAMLNode); -}; diff --git a/packages/node_modules/@node-red/nodes/core/sequence/17-split.html b/packages/node_modules/@node-red/nodes/core/sequence/17-split.html deleted file mode 100644 index 265719d1e..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/17-split.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js b/packages/node_modules/@node-red/nodes/core/sequence/17-split.js deleted file mode 100644 index e158f344c..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js +++ /dev/null @@ -1,785 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function sendArray(node,msg,array,send) { - for (var i = 0; i < array.length-1; i++) { - msg.payload = array[i]; - msg.parts.index = node.c++; - if (node.stream !== true) { msg.parts.count = array.length; } - send(RED.util.cloneMessage(msg)); - } - if (node.stream !== true) { - msg.payload = array[i]; - msg.parts.index = node.c++; - msg.parts.count = array.length; - send(RED.util.cloneMessage(msg)); - node.c = 0; - } - else { node.remainder = array[i]; } - } - - function SplitNode(n) { - RED.nodes.createNode(this,n); - var node = this; - node.stream = n.stream; - node.spltType = n.spltType || "str"; - node.addname = n.addname || ""; - try { - if (node.spltType === "str") { - this.splt = (n.splt || "\\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0"); - } else if (node.spltType === "bin") { - var spltArray = JSON.parse(n.splt); - if (Array.isArray(spltArray)) { - this.splt = Buffer.from(spltArray); - } else { - throw new Error("not an array"); - } - this.spltBuffer = spltArray; - } else if (node.spltType === "len") { - this.splt = parseInt(n.splt); - if (isNaN(this.splt) || this.splt < 1) { - throw new Error("invalid split length: "+n.splt); - } - } - this.arraySplt = (n.arraySplt === undefined)?1:parseInt(n.arraySplt); - if (isNaN(this.arraySplt) || this.arraySplt < 1) { - throw new Error("invalid array split length: "+n.arraySplt); - } - } catch(err) { - this.error("Invalid split property: "+err.toString()); - return; - } - node.c = 0; - node.buffer = Buffer.from([]); - node.pendingDones = []; - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("payload")) { - if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack - else { msg.parts = {}; } - msg.parts.id = RED.util.generateId(); // generate a random id - delete msg._msgid; - if (typeof msg.payload === "string") { // Split String into array - msg.payload = (node.remainder || "") + msg.payload; - msg.parts.type = "string"; - if (node.spltType === "len") { - msg.parts.ch = ""; - msg.parts.len = node.splt; - var count = msg.payload.length/node.splt; - if (Math.floor(count) !== count) { - count = Math.ceil(count); - } - if (node.stream !== true) { - msg.parts.count = count; - node.c = 0; - } - var pos = 0; - var data = msg.payload; - for (var i=0; i 1) { - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - node.remainder = data.substring(pos); - if ((node.stream !== true) || (node.remainder.length === node.splt)) { - msg.payload = node.remainder; - msg.parts.index = node.c++; - send(RED.util.cloneMessage(msg)); - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - done(); - node.remainder = ""; - } else { - node.pendingDones.push(done); - } - } - else { - var a = []; - if (node.spltType === "bin") { - if (!node.spltBufferString) { - node.spltBufferString = node.splt.toString(); - } - a = msg.payload.split(node.spltBufferString); - msg.parts.ch = node.spltBuffer; // pass the split char to other end for rejoin - } else if (node.spltType === "str") { - a = msg.payload.split(node.splt); - msg.parts.ch = node.splt; // pass the split char to other end for rejoin - } - sendArray(node,msg,a,send); - done(); - } - } - else if (Array.isArray(msg.payload)) { // then split array into messages - msg.parts.type = "array"; - var count = msg.payload.length/node.arraySplt; - if (Math.floor(count) !== count) { - count = Math.ceil(count); - } - msg.parts.count = count; - var pos = 0; - var data = msg.payload; - msg.parts.len = node.arraySplt; - for (var i=0; i 1) { - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - node.buffer = buff.slice(pos); - if ((node.stream !== true) || (node.buffer.length === node.splt)) { - msg.payload = node.buffer; - msg.parts.index = node.c++; - send(RED.util.cloneMessage(msg)); - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - done(); - node.buffer = Buffer.from([]); - } else { - node.pendingDones.push(done); - } - } - else { - var count = 0; - if (node.spltType === "bin") { - msg.parts.ch = node.spltBuffer; - } else if (node.spltType === "str") { - msg.parts.ch = node.splt; - } - var pos = buff.indexOf(node.splt); - var end; - while (pos > -1) { - count++; - end = pos+node.splt.length; - pos = buff.indexOf(node.splt,end); - } - count++; - if (node.stream !== true) { - msg.parts.count = count; - node.c = 0; - } - var i = 0, p = 0; - pos = buff.indexOf(node.splt); - while (pos > -1) { - msg.payload = buff.slice(p,pos); - msg.parts.index = node.c++; - send(RED.util.cloneMessage(msg)); - i++; - p = pos+node.splt.length; - pos = buff.indexOf(node.splt,p); - } - if (count > 1) { - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - if ((node.stream !== true) && (p < buff.length)) { - msg.payload = buff.slice(p,buff.length); - msg.parts.index = node.c++; - msg.parts.count = node.c++; - send(RED.util.cloneMessage(msg)); - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - else { - node.buffer = buff.slice(p,buff.length); - node.pendingDones.push(done); - } - if (node.buffer.length == 0) { - done(); - } - } - } else { // otherwise drop the message. - done(); - } - } - }); - } - RED.nodes.registerType("split",SplitNode); - - - var _maxKeptMsgsCount; - - function maxKeptMsgsCount(node) { - if (_maxKeptMsgsCount === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _maxKeptMsgsCount = RED.settings[name]; - } - else { - _maxKeptMsgsCount = 0; - } - } - return _maxKeptMsgsCount; - } - - function applyReduce(exp, accum, msg, index, count, done) { - exp.assign("I", index); - exp.assign("N", count); - exp.assign("A", accum); - RED.util.evaluateJSONataExpression(exp, msg, done); - } - - function exp_or_undefined(exp) { - if((exp === "") || - (exp === null)) { - return undefined; - } - return exp - } - - - function reduceMessageGroup(node,msgInfos,exp,fixup,count,accumulator,done) { - var msgInfo = msgInfos.shift(); - exp.assign("I", msgInfo.msg.parts.index); - exp.assign("N", count); - exp.assign("A", accumulator); - RED.util.evaluateJSONataExpression(exp, msgInfo.msg, (err,result) => { - if (err) { - return done(err); - } - if (msgInfos.length === 0) { - if (fixup) { - fixup.assign("N", count); - fixup.assign("A", result); - RED.util.evaluateJSONataExpression(fixup, {}, (err, result) => { - if (err) { - return done(err); - } - msgInfo.send({payload: result}); - done(); - }); - } else { - msgInfo.send({payload: result}); - done(); - } - } else { - reduceMessageGroup(node,msgInfos,exp,fixup,count,result,done); - } - }); - } - function reduceAndSendGroup(node, group, done) { - var is_right = node.reduce_right; - var flag = is_right ? -1 : 1; - var msgInfos = group.msgs; - const preservedMsgInfos = [...msgInfos]; - try { - RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => { - var reduceExpression = node.reduceExpression; - var fixupExpression = node.fixupExpression; - var count = group.count; - msgInfos.sort(function(x,y) { - var ix = x.msg.parts.index; - var iy = y.msg.parts.index; - if (ix < iy) {return -flag;} - if (ix > iy) {return flag;} - return 0; - }); - reduceMessageGroup(node, msgInfos,reduceExpression,fixupExpression,count,accum,(err,result) => { - if (err) { - preservedMsgInfos.pop(); // omit last message to emit error message - preservedMsgInfos.forEach(mInfo => mInfo.done()); - done(err); - return; - } else { - preservedMsgInfos.forEach(mInfo => mInfo.done()); - done(); - } - }) - }); - } catch(err) { - done(new Error(RED._("join.errors.invalid-expr",{error:err.message}))); - } - } - - function reduceMessage(node, msgInfo, done) { - let msg = msgInfo.msg; - if (msg.hasOwnProperty('parts')) { - var parts = msg.parts; - var pending = node.pending; - var pending_count = node.pending_count; - var gid = msg.parts.id; - var count; - if (!pending.hasOwnProperty(gid)) { - if(parts.hasOwnProperty('count')) { - count = msg.parts.count; - } - pending[gid] = { - count: count, - msgs: [] - }; - } - var group = pending[gid]; - var msgs = group.msgs; - if (parts.hasOwnProperty('count') && (group.count === undefined)) { - group.count = parts.count; - } - msgs.push(msgInfo); - pending_count++; - var completeProcess = function(err) { - if (err) { - return done(err); - } - node.pending_count = pending_count; - var max_msgs = maxKeptMsgsCount(node); - if ((max_msgs > 0) && (pending_count > max_msgs)) { - Object.values(node.pending).forEach(group => { - group.msgs.forEach(mInfo => { - if (mInfo.msg._msgid !== msgInfo.msg._msgid) { - mInfo.done(); - } - }); - }); - node.pending = {}; - node.pending_count = 0; - done(RED._("join.too-many")); - return; - } - return done(); - } - if (msgs.length === group.count) { - delete pending[gid]; - pending_count -= msgs.length; - reduceAndSendGroup(node, group, completeProcess) - } else { - completeProcess(); - } - } else { - msgInfo.send(msg); - msgInfo.done(); - done(); - } - } - - function JoinNode(n) { - RED.nodes.createNode(this,n); - this.mode = n.mode||"auto"; - this.property = n.property||"payload"; - this.propertyType = n.propertyType||"msg"; - if (this.propertyType === 'full') { - this.property = "payload"; - } - this.key = n.key||"topic"; - this.timer = (this.mode === "auto") ? 0 : Number(n.timeout || 0)*1000; - this.count = Number(n.count || 0); - this.joiner = n.joiner||""; - this.joinerType = n.joinerType||"str"; - - this.reduce = (this.mode === "reduce"); - if (this.reduce) { - this.exp_init = n.reduceInit; - this.exp_init_type = n.reduceInitType; - var exp_reduce = n.reduceExp; - var exp_fixup = exp_or_undefined(n.reduceFixup); - this.reduce_right = n.reduceRight; - try { - this.reduceExpression = RED.util.prepareJSONataExpression(exp_reduce, this); - this.fixupExpression = (exp_fixup !== undefined) ? RED.util.prepareJSONataExpression(exp_fixup, this) : undefined; - } catch(e) { - this.error(RED._("join.errors.invalid-expr",{error:e.message})); - return; - } - } - - if (this.joinerType === "str") { - this.joiner = this.joiner.replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0"); - } else if (this.joinerType === "bin") { - var joinArray = JSON.parse(n.joiner || "[]"); - if (Array.isArray(joinArray)) { - this.joiner = Buffer.from(joinArray); - } else { - throw new Error("not an array"); - } - } - - this.build = n.build || "array"; - this.accumulate = n.accumulate || "false"; - - this.output = n.output || "stream"; - this.pending = {}; - this.pending_count = 0; - - //this.topic = n.topic; - var node = this; - var inflight = {}; - - var completeSend = function(partId) { - var group = inflight[partId]; - if (group.timeout) { clearTimeout(group.timeout); } - if ((node.accumulate !== true) || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; } - if (group.type === 'array' && group.arrayLen > 1) { - var newArray = []; - group.payload.forEach(function(n) { - newArray = newArray.concat(n); - }) - group.payload = newArray; - } - else if (group.type === 'buffer') { - var buffers = []; - var bufferLen = 0; - if (group.joinChar !== undefined) { - var joinBuffer = Buffer.from(group.joinChar); - for (var i=0; i 0) { - buffers.push(joinBuffer); - bufferLen += joinBuffer.length; - } - if (!Buffer.isBuffer(group.payload[i])) { - group.payload[i] = Buffer.from(group.payload[i]); - } - buffers.push(group.payload[i]); - bufferLen += group.payload[i].length; - } - } - else { - bufferLen = group.bufferLen; - buffers = group.payload; - } - group.payload = Buffer.concat(buffers,bufferLen); - } - - if (group.type === 'string') { - var groupJoinChar = group.joinChar; - if (typeof group.joinChar !== 'string') { - groupJoinChar = group.joinChar.toString(); - } - RED.util.setMessageProperty(group.msg,node.property,group.payload.join(groupJoinChar)); - } - else { - if (node.propertyType === 'full') { - group.msg = RED.util.cloneMessage(group.msg); - } - RED.util.setMessageProperty(group.msg,node.property,group.payload); - } - if (group.msg.hasOwnProperty('parts') && group.msg.parts.hasOwnProperty('parts')) { - group.msg.parts = group.msg.parts.parts; - } - else { - delete group.msg.parts; - } - delete group.msg.complete; - group.send(RED.util.cloneMessage(group.msg)); - group.dones.forEach(f => f()); - group.dones = []; - } - - var pendingMessages = []; - var activeMessage = null; - // In reduce mode, we must process messages fully in order otherwise - // groups may overlap and cause unexpected results. The use of JSONata - // means some async processing *might* occur if flow/global context is - // accessed. - var processReduceMessageQueue = function(msgInfo) { - if (msgInfo) { - // A new message has arrived - add it to the message queue - pendingMessages.push(msgInfo); - if (activeMessage !== null) { - // The node is currently processing a message, so do nothing - // more with this message - return; - } - } - if (pendingMessages.length === 0) { - // There are no more messages to process, clear the active flag - // and return - activeMessage = null; - return; - } - - // There are more messages to process. Get the next message and - // start processing it. Recurse back in to check for any more - var nextMsgInfo = pendingMessages.shift(); - activeMessage = true; - reduceMessage(node, nextMsgInfo, err => { - if (err) { - nextMsgInfo.done(err);//.error(err,nextMsg); - } - activeMessage = null; - processReduceMessageQueue(); - }) - } - - this.on("input", function(msg, send, done) { - try { - var property; - var partId = "_"; - if (node.propertyType == "full") { - property = msg; - } - else { - try { - property = RED.util.getMessageProperty(msg,node.property); - } catch(err) { - node.warn("Message property "+node.property+" not found"); - done(); - return; - } - } - - if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) { - // if a blank reset messag erest it all. - if (msg.hasOwnProperty("reset")) { - if (inflight && inflight.hasOwnProperty("partId") && inflight[partId].timeout) { - clearTimeout(inflight[partId].timeout); - } - inflight = {}; - } - else { - node.warn("Message missing msg.parts property - cannot join in 'auto' mode") - } - done(); - return; - } - - var payloadType; - var propertyKey; - var targetCount; - var joinChar; - var arrayLen; - var propertyIndex; - if (node.mode === "auto") { - // Use msg.parts to identify all of the group information - partId = msg.parts.id; - payloadType = msg.parts.type; - targetCount = msg.parts.count; - joinChar = msg.parts.ch; - propertyKey = msg.parts.key; - arrayLen = msg.parts.len; - propertyIndex = msg.parts.index; - } - else if (node.mode === 'reduce') { - return processReduceMessageQueue({msg, send, done}); - } - else { - // Use the node configuration to identify all of the group information - payloadType = node.build; - targetCount = node.count; - joinChar = node.joiner; - if (n.count === "" && msg.hasOwnProperty('parts')) { - targetCount = msg.parts.count || 0; - } - if (node.build === 'object') { - propertyKey = RED.util.getMessageProperty(msg,node.key); - } - } - - if (msg.hasOwnProperty("restartTimeout")) { - if (inflight[partId]) { - if (inflight[partId].timeout) { - clearTimeout(inflight[partId].timeout); - } - if (node.timer > 0) { - inflight[partId].timeout = setTimeout(function() { - completeSend(partId) - }, node.timer) - } - } - } - - if (msg.hasOwnProperty("reset")) { - if (inflight[partId]) { - if (inflight[partId].timeout) { - clearTimeout(inflight[partId].timeout); - } - inflight[partId].dones.forEach(f => f()); - delete inflight[partId] - } - done(); - return; - } - - if ((payloadType === 'object') && (propertyKey === null || propertyKey === undefined || propertyKey === "")) { - if (node.mode === "auto") { - node.warn("Message missing 'msg.parts.key' property - cannot add to object"); - } - else { - if (msg.hasOwnProperty('complete')) { - if (inflight[partId]) { - inflight[partId].msg.complete = msg.complete; - inflight[partId].send = send; - completeSend(partId); - } - } - else { - node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object") - } - } - done(); - return; - } - - if (!inflight.hasOwnProperty(partId)) { - if (payloadType === 'object' || payloadType === 'merged') { - inflight[partId] = { - currentCount:0, - payload:{}, - targetCount:targetCount, - type:"object", - msg:RED.util.cloneMessage(msg), - send: send, - dones: [] - }; - } - else { - inflight[partId] = { - currentCount:0, - payload:[], - targetCount:targetCount, - type:payloadType, - msg:RED.util.cloneMessage(msg), - send: send, - dones: [] - }; - if (payloadType === 'string') { - inflight[partId].joinChar = joinChar; - } else if (payloadType === 'array') { - inflight[partId].arrayLen = arrayLen; - } else if (payloadType === 'buffer') { - inflight[partId].bufferLen = 0; - inflight[partId].joinChar = joinChar; - } - } - if (node.timer > 0) { - inflight[partId].timeout = setTimeout(function() { - completeSend(partId) - }, node.timer) - } - } - inflight[partId].dones.push(done); - - var group = inflight[partId]; - if (payloadType === 'buffer') { - if (property !== undefined) { - if (Buffer.isBuffer(property) || (typeof property === "string") || Array.isArray(property)) { - inflight[partId].bufferLen += property.length; - } - else { - done(RED._("join.errors.invalid-type",{error:(typeof property)})); - return; - } - } - } - if (payloadType === 'object') { - group.payload[propertyKey] = property; - group.currentCount = Object.keys(group.payload).length; - } else if (payloadType === 'merged') { - if (Array.isArray(property) || typeof property !== 'object') { - if (!msg.hasOwnProperty("complete")) { - node.warn("Cannot merge non-object types"); - } - } else { - for (propertyKey in property) { - if (property.hasOwnProperty(propertyKey) && propertyKey !== '_msgid') { - group.payload[propertyKey] = property[propertyKey]; - } - } - group.currentCount = Object.keys(group.payload).length; - //group.currentCount++; - } - } else { - if (!isNaN(propertyIndex)) { - if (group.payload[propertyIndex] == undefined) { group.currentCount++; } - group.payload[propertyIndex] = property; - } else { - if (property !== undefined) { - group.payload.push(property); - group.currentCount++; - } - } - } - group.msg = Object.assign(group.msg, msg); - group.send = send; - var tcnt = group.targetCount; - if (msg.hasOwnProperty("parts")) { - tcnt = group.targetCount || msg.parts.count; - group.targetCount = tcnt; - } - if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) { - completeSend(partId); - } - } - catch(err) { - done(err); - console.log(err.stack); - } - }); - - this.on("close", function() { - for (var i in inflight) { - if (inflight.hasOwnProperty(i)) { - clearTimeout(inflight[i].timeout); - inflight[i].dones.forEach(d => d()); - } - } - }); - } - RED.nodes.registerType("join",JoinNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.html b/packages/node_modules/@node-red/nodes/core/sequence/18-sort.html deleted file mode 100644 index fd27c8b54..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.js b/packages/node_modules/@node-red/nodes/core/sequence/18-sort.js deleted file mode 100644 index 3bcdfb105..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.js +++ /dev/null @@ -1,266 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var _max_kept_msgs_count; - - function max_kept_msgs_count(node) { - if (_max_kept_msgs_count === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _max_kept_msgs_count = RED.settings[name]; - } - else { - _max_kept_msgs_count = 0; - } - } - return _max_kept_msgs_count; - } - - // function get_context_val(node, name, dval) { - // var context = node.context(); - // var val = context.get(name); - // if (val === undefined) { - // context.set(name, dval); - // return dval; - // } - // return val; - // } - - function SortNode(n) { - RED.nodes.createNode(this, n); - var node = this; - var pending = {};//get_context_val(node, 'pending', {}) - var pending_count = 0; - var pending_id = 0; - var order = n.order || "ascending"; - var as_num = n.as_num || false; - var target_prop = n.target || "payload"; - var target_is_prop = (n.targetType === 'msg'); - var key_is_exp = target_is_prop ? (n.msgKeyType === "jsonata") : (n.seqKeyType === "jsonata"); - var key_prop = n.seqKey || "payload"; - var key_exp = target_is_prop ? n.msgKey : n.seqKey; - - if (key_is_exp) { - try { - key_exp = RED.util.prepareJSONataExpression(key_exp, this); - } - catch (e) { - node.error(RED._("sort.invalid-exp",{message:e.toString()})); - return; - } - } - var dir = (order === "ascending") ? 1 : -1; - var conv = as_num ? function(x) { return Number(x); } - : function(x) { return x; }; - - function generateComparisonFunction(key) { - return function(x, y) { - var xp = conv(key(x)); - var yp = conv(key(y)); - if (xp === yp) { return 0; } - if (xp > yp) { return dir; } - return -dir; - }; - } - - function sortMessageGroup(group) { - var promise; - var msgInfos = group.msgInfos; - if (key_is_exp) { - var evaluatedDataPromises = msgInfos.map(mInfo => { - return new Promise((resolve,reject) => { - RED.util.evaluateJSONataExpression(key_exp, mInfo.msg, (err, result) => { - if (err) { - reject(RED._("sort.invalid-exp",{message:err.toString()})); - } else { - resolve({ - item: mInfo, - sortValue: result - }) - } - }); - }) - }); - promise = Promise.all(evaluatedDataPromises).then(evaluatedElements => { - // Once all of the sort keys are evaluated, sort by them - var comp = generateComparisonFunction(elem=>elem.sortValue); - return evaluatedElements.sort(comp).map(elem=>elem.item); - }); - } else { - var key = function(msg) { - return ; - } - var comp = generateComparisonFunction(mInfo => RED.util.getMessageProperty(mInfo.msg, key_prop)); - try { - msgInfos.sort(comp); - } - catch (e) { - return; // not send when error - } - promise = Promise.resolve(msgInfos); - } - return promise.then(msgInfos => { - for (let i = 0; i < msgInfos.length; i++) { - const msg = msgInfos[i].msg; - msg.parts.index = i; - msgInfos[i].send(msg); - msgInfos[i].done(); - } - }); - } - - function sortMessageProperty(msg) { - var data = RED.util.getMessageProperty(msg, target_prop); - if (Array.isArray(data)) { - if (key_is_exp) { - // key is an expression. Evaluated the expression for each item - // to get its sort value. As this could be async, need to do - // it first. - var evaluatedDataPromises = data.map(elem => { - return new Promise((resolve,reject) => { - RED.util.evaluateJSONataExpression(key_exp, elem, (err, result) => { - if (err) { - reject(RED._("sort.invalid-exp",{message:err.toString()})); - } else { - resolve({ - item: elem, - sortValue: result - }) - } - }); - }) - }) - return Promise.all(evaluatedDataPromises).then(evaluatedElements => { - // Once all of the sort keys are evaluated, sort by them - // and reconstruct the original message item with the newly - // sorted values. - var comp = generateComparisonFunction(elem=>elem.sortValue); - data = evaluatedElements.sort(comp).map(elem=>elem.item); - RED.util.setMessageProperty(msg, target_prop,data); - return true; - }) - } else { - var comp = generateComparisonFunction(elem=>elem); - try { - data.sort(comp); - } catch (e) { - return Promise.resolve(false); - } - return Promise.resolve(true); - } - } - return Promise.resolve(false); - } - - function removeOldestPending() { - var oldest; - var oldest_key; - for(var key in pending) { - if (pending.hasOwnProperty(key)) { - var item = pending[key]; - if((oldest === undefined) || - (oldest.seq_no > item.seq_no)) { - oldest = item; - oldest_key = key; - } - } - } - if(oldest !== undefined) { - oldest.msgInfos[oldest.msgInfos.length - 1].done(RED._("sort.too-many")); - for (let i = 0; i < oldest.msgInfos.length - 1; i++) { - oldest.msgInfos[i].done(); - } - delete pending[oldest_key]; - return oldest.msgInfos.length; - } - return 0; - } - - function processMessage(msgInfo) { - const msg = msgInfo.msg; - if (target_is_prop) { - sortMessageProperty(msg).then(send => { - if (send) { - msgInfo.send(msg); - } - msgInfo.done(); - }).catch(err => { - msgInfo.done(err); - }); - return; - } - var parts = msg.parts; - if (!parts || !parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) { - msgInfo.done(); - return; - } - var gid = parts.id; - if (!pending.hasOwnProperty(gid)) { - pending[gid] = { - count: undefined, - msgInfos: [], - seq_no: pending_id++ - }; - } - var group = pending[gid]; - var msgInfos = group.msgInfos; - msgInfos.push(msgInfo); - if (parts.hasOwnProperty("count")) { - group.count = parts.count; - } - pending_count++; - if (group.count === msgInfos.length) { - delete pending[gid] - sortMessageGroup(group).catch(err => { - // throw an error for last message, and just call done() for remaining messages - msgInfos[msgInfos.length-1].done(err); - for (let i = 0; i < msgInfos.length - 1; i++) { - msgInfos[i].done() - }; - }); - pending_count -= msgInfos.length; - } else { - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (pending_count > max_msgs)) { - pending_count -= removeOldestPending(); - } - } - } - - this.on("input", function(msg, send, done) { - processMessage({msg, send, done}); - }); - - this.on("close", function() { - for(var key in pending) { - if (pending.hasOwnProperty(key)) { - node.log(RED._("sort.clear"), pending[key].msgInfos[0]); - const group = pending[key]; - group.msgInfos.forEach(mInfo => { - mInfo.done(); - }); - delete pending[key]; - } - } - pending_count = 0; - }); - } - - RED.nodes.registerType("sort", SortNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html deleted file mode 100644 index 418ac605b..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js deleted file mode 100644 index f3f29df6a..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js +++ /dev/null @@ -1,311 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var _max_kept_msgs_count = undefined; - - function max_kept_msgs_count(node) { - if (_max_kept_msgs_count === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _max_kept_msgs_count = RED.settings[name]; - } - else { - _max_kept_msgs_count = 0; - } - } - return _max_kept_msgs_count; - } - - function send_msgs(node, msgInfos, clone_msg) { - var count = msgInfos.length; - var msg_id = msgInfos[0].msg._msgid; - for (var i = 0; i < count; i++) { - var msg = clone_msg ? RED.util.cloneMessage(msgInfos[i].msg) : msgInfos[i].msg; - if (!msg.hasOwnProperty("parts")) { - msg.parts = {}; - } - var parts = msg.parts; - parts.id = msg_id; - parts.index = i; - parts.count = count; - msgInfos[i].send(msg); - //msgInfos[i].done(); - } - } - - function send_interval(node, allow_empty_seq) { - let msgInfos = node.pending; - if (msgInfos.length > 0) { - send_msgs(node, msgInfos, false); - msgInfos.forEach(e => e.done()); - node.pending = []; - } - else { - if (allow_empty_seq) { - let mid = RED.util.generateId(); - let msg = { - payload: null, - parts: { - id: mid, - index: 0, - count: 1 - } - }; - node.send(msg); - } - } - } - - function is_complete(pending, topic) { - if (pending.hasOwnProperty(topic)) { - var p_topic = pending[topic]; - var gids = p_topic.gids; - if (gids.length > 0) { - var gid = gids[0]; - var groups = p_topic.groups; - var group = groups[gid]; - return (group.count === group.msgs.length); - } - } - return false; - } - - function get_msgs_of_topic(pending, topic) { - var p_topic = pending[topic]; - var groups = p_topic.groups; - var gids = p_topic.gids; - var gid = gids[0]; - var group = groups[gid]; - return group.msgs; - } - - function remove_topic(pending, topic) { - var p_topic = pending[topic]; - var groups = p_topic.groups; - var gids = p_topic.gids; - var gid = gids.shift(); - delete groups[gid]; - } - - function try_concat(node, pending) { - var topics = node.topics; - for (var topic of topics) { - if (!is_complete(pending, topic)) { - return; - } - } - var msgInfos = []; - for (var topic of topics) { - var t_msgInfos = get_msgs_of_topic(pending, topic); - msgInfos = msgInfos.concat(t_msgInfos); - } - for (var topic of topics) { - remove_topic(pending, topic); - } - send_msgs(node, msgInfos, true); - msgInfos.forEach(e => e.done() ); - node.pending_count -= msgInfos.length; - } - - function add_to_topic_group(pending, topic, gid, msgInfo) { - if (!pending.hasOwnProperty(topic)) { - pending[topic] = { groups: {}, gids: [] }; - } - var p_topic = pending[topic]; - var groups = p_topic.groups; - var gids = p_topic.gids; - if (!groups.hasOwnProperty(gid)) { - groups[gid] = { msgs: [], count: undefined }; - gids.push(gid); - } - var group = groups[gid]; - group.msgs.push(msgInfo); - if ((group.count === undefined) && - msgInfo.msg.parts.hasOwnProperty('count')) { - group.count = msgInfo.msg.parts.count; - } - } - - function concat_msg(node, msg, send, done) { - var topic = msg.topic; - if(node.topics.indexOf(topic) >= 0) { - if (!msg.hasOwnProperty("parts") || - !msg.parts.hasOwnProperty("id") || - !msg.parts.hasOwnProperty("index") || - !msg.parts.hasOwnProperty("count")) { - done(RED._("batch.no-parts")); - return; - } - var gid = msg.parts.id; - var pending = node.pending; - add_to_topic_group(pending, topic, gid, {msg, send, done}); - node.pending_count++; - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - Object.values(node.pending).forEach(p_topic => { - Object.values(p_topic.groups).forEach(group => { - group.msgs.forEach(msgInfo => { - if (msgInfo.msg.id === msg.id) { - // the message that caused the overflow - msgInfo.done(RED._("batch.too-many")); - } else { - msgInfo.done(); - } - }) - }) - }); - node.pending = {}; - node.pending_count = 0; - } - try_concat(node, pending); - } - } - - function BatchNode(n) { - RED.nodes.createNode(this,n); - var node = this; - var mode = n.mode || "count"; - - node.pending_count = 0; - if (mode === "count") { - var count = Number(n.count || 1); - var overlap = Number(n.overlap || 0); - var is_overlap = (overlap > 0); - if (count <= overlap) { - node.error(RED._("batch.count.invalid")); - return; - } - node.pending = []; - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - done(); - return; - } - var queue = node.pending; - queue.push({msg, send, done}); - node.pending_count++; - if (queue.length === count) { - send_msgs(node, queue, is_overlap); - for (let i = 0; i < queue.length-overlap; i++) { - queue[i].done(); - } - node.pending = - (overlap === 0) ? [] : queue.slice(-overlap); - node.pending_count = 0; - } - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - let lastMInfo = node.pending.pop(); - lastMInfo.done(RED._("batch.too-many")); - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - } - }); - this.on("close", function() { - node.pending.forEach(e=> e.done()); - node.pending_count = 0; - node.pending = []; - }); - } - else if (mode === "interval") { - var interval = Number(n.interval || "0") *1000; - var allow_empty_seq = n.allowEmptySequence; - node.pending = [] - function msgHandler() { - send_interval(node, allow_empty_seq); - node.pending_count = 0; - } - var timer = undefined; - if (interval > 0) { - timer = setInterval(msgHandler, interval); - } - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - if (timer !== undefined) { - clearInterval(timer); - } - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - done(); - if (interval > 0) { - timer = setInterval(msgHandler, interval); - } - return; - } - node.pending.push({msg, send, done}); - node.pending_count++; - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - let lastMInfo = node.pending.pop(); - lastMInfo.done(RED._("batch.too-many")); - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - } - }); - this.on("close", function() { - if (timer !== undefined) { - clearInterval(timer); - } - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - }); - } - else if(mode === "concat") { - node.topics = (n.topics || []).map(function(x) { - return x.topic; - }); - node.pending = {}; - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - Object.values(node.pending).forEach(p_topic => { - Object.values(p_topic.groups).forEach(group => { - group.msgs.forEach(e => e.done()); - }); - }); - node.pending = {}; - node.pending_count = 0; - done(); - return; - } - concat_msg(node, msg, send, done); - }); - this.on("close", function() { - Object.values(node.pending).forEach(p_topic => { - Object.values(p_topic.groups).forEach(group => { - group.msgs.forEach(e => e.done()); - }); - }); - node.pending = {}; - node.pending_count = 0; - }); - } - else { - node.error(RED._("batch.unexpected")); - } - } - - RED.nodes.registerType("batch", BatchNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.html b/packages/node_modules/@node-red/nodes/core/storage/10-file.html deleted file mode 100755 index b76a01615..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.html +++ /dev/null @@ -1,347 +0,0 @@ - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.js b/packages/node_modules/@node-red/nodes/core/storage/10-file.js deleted file mode 100644 index 079b4e82f..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.js +++ /dev/null @@ -1,401 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var fs = require("fs-extra"); - var os = require("os"); - var path = require("path"); - var iconv = require("iconv-lite") - - function encode(data, enc) { - if (enc !== "none") { - return iconv.encode(data, enc); - } - return Buffer.from(data); - } - - function decode(data, enc) { - if (enc !== "none") { - return iconv.decode(data, enc); - } - return data.toString(); - } - - function FileNode(n) { - // Write/delete a file - RED.nodes.createNode(this,n); - this.filename = n.filename; - this.appendNewline = n.appendNewline; - this.overwriteFile = n.overwriteFile.toString(); - this.createDir = n.createDir || false; - this.encoding = n.encoding || "none"; - var node = this; - node.wstream = null; - node.msgQueue = []; - node.closing = false; - node.closeCallback = null; - - function processMsg(msg,nodeSend, done) { - var filename = node.filename || msg.filename || ""; - var fullFilename = filename; - if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) { - fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename)); - } - if ((!node.filename) && (!node.tout)) { - node.tout = setTimeout(function() { - node.status({fill:"grey",shape:"dot",text:filename}); - clearTimeout(node.tout); - node.tout = null; - },333); - } - if (filename === "") { - node.warn(RED._("file.errors.nofilename")); - done(); - } else if (node.overwriteFile === "delete") { - fs.unlink(fullFilename, function (err) { - if (err) { - node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg); - } - else { - if (RED.settings.verbose) { - node.log(RED._("file.status.deletedfile",{file:filename})); - } - nodeSend(msg); - } - done(); - }); - } else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) { - var dir = path.dirname(fullFilename); - if (node.createDir) { - try { - fs.ensureDirSync(dir); - } - catch(err) { - node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); - done(); - return; - } - } - - var data = msg.payload; - if ((typeof data === "object") && (!Buffer.isBuffer(data))) { - data = JSON.stringify(data); - } - if (typeof data === "boolean") { data = data.toString(); } - if (typeof data === "number") { data = data.toString(); } - if ((node.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; } - var buf; - if (node.encoding === "setbymsg") { - buf = encode(data, msg.encoding || "none"); - } - else { buf = encode(data, node.encoding); } - if (node.overwriteFile === "true") { - var wstream = fs.createWriteStream(fullFilename, { encoding:'binary', flags:'w', autoClose:true }); - node.wstream = wstream; - wstream.on("error", function(err) { - node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); - done(); - }); - wstream.on("open", function() { - wstream.once("close", function() { - nodeSend(msg); - done(); - }); - wstream.end(buf); - }) - return; - } - else { - // Append mode - var recreateStream = !node.wstream || !node.filename; - if (node.wstream && node.wstreamIno) { - // There is already a stream open and we have the inode - // of the file. Check the file hasn't been deleted - // or deleted and recreated. - try { - var stat = fs.statSync(fullFilename); - // File exists - check the inode matches - if (stat.ino !== node.wstreamIno) { - // The file has been recreated. Close the current - // stream and recreate it - recreateStream = true; - node.wstream.end(); - delete node.wstream; - delete node.wstreamIno; - } - } - catch(err) { - // File does not exist - recreateStream = true; - node.wstream.end(); - delete node.wstream; - delete node.wstreamIno; - } - } - if (recreateStream) { - node.wstream = fs.createWriteStream(fullFilename, { encoding:'binary', flags:'a', autoClose:true }); - node.wstream.on("open", function(fd) { - try { - var stat = fs.statSync(fullFilename); - node.wstreamIno = stat.ino; - } catch(err) { - } - }); - node.wstream.on("error", function(err) { - node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); - done(); - }); - } - if (node.filename) { - // Static filename - write and reuse the stream next time - node.wstream.write(buf, function() { - nodeSend(msg); - done(); - }); - } - else { - // Dynamic filename - write and close the stream - node.wstream.once("close", function() { - nodeSend(msg); - delete node.wstream; - delete node.wstreamIno; - done(); - }); - node.wstream.end(buf); - } - } - } - else { - done(); - } - } - - function processQueue(queue) { - var event = queue[0]; - processMsg(event.msg, event.send, function() { - event.done(); - queue.shift(); - if (queue.length > 0) { - processQueue(queue); - } - else if (node.closing) { - closeNode(); - } - }); - } - - this.on("input", function(msg,nodeSend,nodeDone) { - var msgQueue = node.msgQueue; - msgQueue.push({ - msg: msg, - send: nodeSend, - done: nodeDone - }) - if (msgQueue.length > 1) { - // pending write exists - return; - } - try { - processQueue(msgQueue); - } - catch (e) { - node.msgQueue = []; - if (node.closing) { - closeNode(); - } - throw e; - } - }); - - function closeNode() { - if (node.wstream) { node.wstream.end(); } - if (node.tout) { clearTimeout(node.tout); } - node.status({}); - var cb = node.closeCallback; - node.closeCallback = null; - node.closing = false; - if (cb) { - cb(); - } - } - - this.on('close', function(done) { - if (node.closing) { - // already closing - return; - } - node.closing = true; - if (done) { - node.closeCallback = done; - } - if (node.msgQueue.length > 0) { - // close after queue processed - return; - } - else { - closeNode(); - } - }); - } - RED.nodes.registerType("file",FileNode); - - - function FileInNode(n) { - // Read a file - RED.nodes.createNode(this,n); - this.filename = n.filename; - this.format = n.format; - this.chunk = false; - this.encoding = n.encoding || "none"; - this.allProps = n.allProps || false; - if (n.sendError === undefined) { - this.sendError = true; - } else { - this.sendError = n.sendError; - } - if (this.format === "lines") { this.chunk = true; } - if (this.format === "stream") { this.chunk = true; } - var node = this; - - this.on("input",function(msg, nodeSend, nodeDone) { - var filename = (node.filename || msg.filename || "").replace(/\t|\r|\n/g,''); - var fullFilename = filename; - if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) { - fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename)); - } - if (!node.filename) { - node.status({fill:"grey",shape:"dot",text:filename}); - } - if (filename === "") { - node.warn(RED._("file.errors.nofilename")); - nodeDone(); - } - else { - msg.filename = filename; - var lines = Buffer.from([]); - var spare = ""; - var count = 0; - var type = "buffer"; - var ch = ""; - if (node.format === "lines") { - ch = "\n"; - type = "string"; - } - var getout = false; - - var rs = fs.createReadStream(fullFilename) - .on('readable', function () { - var chunk; - var m; - var hwm = rs._readableState.highWaterMark; - while (null !== (chunk = rs.read())) { - if (node.chunk === true) { - getout = true; - if (node.format === "lines") { - spare += decode(chunk, node.encoding); - var bits = spare.split("\n"); - for (var i=0; i < bits.length - 1; i++) { - m = {}; - if (node.allProps == true) { - m = RED.util.cloneMessage(msg); - } - else { - m.topic = msg.topic; - m.filename = msg.filename; - } - m.payload = bits[i]; - m.parts= {index:count, ch:ch, type:type, id:msg._msgid} - count += 1; - nodeSend(m); - } - spare = bits[i]; - } - if (node.format === "stream") { - m = {}; - if (node.allProps == true) { - m = RED.util.cloneMessage(msg); - } - else { - m.topic = msg.topic; - m.filename = msg.filename; - } - m.payload = chunk; - m.parts = {index:count, ch:ch, type:type, id:msg._msgid} - count += 1; - if (chunk.length < hwm) { // last chunk is smaller that high water mark = eof - getout = false; - m.parts.count = count; - } - nodeSend(m); - } - } - else { - lines = Buffer.concat([lines,chunk]); - } - } - }) - .on('error', function(err) { - node.error(err, msg); - if (node.sendError) { - var sendMessage = RED.util.cloneMessage(msg); - delete sendMessage.payload; - sendMessage.error = err; - nodeSend(sendMessage); - } - nodeDone(); - }) - .on('end', function() { - if (node.chunk === false) { - if (node.format === "utf8") { - msg.payload = decode(lines, node.encoding); - } - else { msg.payload = lines; } - nodeSend(msg); - } - else if (node.format === "lines") { - var m = {}; - if (node.allProps) { - m = RED.util.cloneMessage(msg); - } - else { - m.topic = msg.topic; - m.filename = msg.filename; - } - m.payload = spare; - m.parts = { - index: count, - count: count + 1, - ch: ch, - type: type, - id: msg._msgid - } - nodeSend(m); - } - else if (getout) { // last chunk same size as high water mark - have to send empty extra packet. - var m = { parts:{index:count, count:count, ch:ch, type:type, id:msg._msgid} }; - nodeSend(m); - } - nodeDone(); - }); - } - }); - this.on('close', function() { - node.status({}); - }); - } - RED.nodes.registerType("file in",FileInNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/storage/23-watch.html b/packages/node_modules/@node-red/nodes/core/storage/23-watch.html deleted file mode 100644 index eafbd46bc..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/23-watch.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/storage/23-watch.js b/packages/node_modules/@node-red/nodes/core/storage/23-watch.js deleted file mode 100644 index 9b6c4bc62..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/23-watch.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var Notify = require("fs.notify"); - var fs = require("fs"); - var path = require("path"); - - var getAllDirs = function (dir, filelist) { - filelist = filelist || []; - fs.readdirSync(dir).forEach(file => { - try { - if (fs.statSync(path.join(dir, file)).isDirectory() ) { - filelist.push(path.join(dir, file)); - getAllDirs(path.join(dir, file), filelist); - } - } catch (error) { - //should we raise an error? - } - }); - return filelist; - } - - function WatchNode(n) { - RED.nodes.createNode(this,n); - - this.recursive = n.recursive || false; - this.files = (n.files || "").split(","); - for (var f=0; f < this.files.length; f++) { - this.files[f] = this.files[f].trim(); - } - this.p = (this.files.length === 1) ? this.files[0] : JSON.stringify(this.files); - var node = this; - - if (node.recursive) { - for (var fi in node.files) { - if (node.files.hasOwnProperty(fi)) { - node.files = node.files.concat(getAllDirs( node.files[fi])); - } - } - } - - var notifications = new Notify(node.files); - notifications.on('change', function (file, event, fpath) { - var stat; - try { - if (fs.statSync(fpath).isDirectory()) { fpath = path.join(fpath,file); } - stat = fs.statSync(fpath); - } catch(e) { } - var type = "none"; - var msg = { payload:fpath, topic:node.p, file:file, filename:fpath }; - if (stat) { - if (stat.isFile()) { type = "file"; msg.size = stat.size; } - else if (stat.isBlockDevice()) { type = "blockdevice"; } - else if (stat.isCharacterDevice()) { type = "characterdevice"; } - else if (stat.isSocket()) { type = "socket"; } - else if (stat.isFIFO()) { type = "fifo"; } - else if (stat.isDirectory()) { - type = "directory"; - if (node.recursive) { - notifications.add([fpath]); - notifications.add(getAllDirs(fpath)); - } - } - else { type = "n/a"; } - } - msg.type = type; - node.send(msg); - }); - - notifications.on('error', function (error, fpath) { - var msg = { payload:fpath }; - node.error(error,msg); - }); - - this.close = function() { - notifications.close(); - } - } - RED.nodes.registerType("watch",WatchNode); -} diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json b/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json index 85b334e68..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json @@ -1 +0,0 @@ -[{"id":"87bd706a.aec93","type":"comment","z":"3ae4b3d9.1f77bc","name":"Output payload value to debug sidebar","info":"Debug node can be used to output payload value to debug sidebar.","x":230,"y":60,"wires":[]},{"id":"8035b07f.7547e","type":"inject","z":"3ae4b3d9.1f77bc","name":"","props":[{"p":"payload","v":"Hello, World!","vt":"str"},{"p":"topic","v":"","vt":"string"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":210,"y":100,"wires":[["20d1344f.931e3c"]]},{"id":"20d1344f.931e3c","type":"debug","z":"3ae4b3d9.1f77bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":450,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json b/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json index c8cb9c182..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json @@ -1 +0,0 @@ -[{"id":"8c66d039.44465","type":"comment","z":"e6956267.5d174","name":"Output complete object","info":"Debug node can be used to output whole object value to debug sidebar.","x":160,"y":60,"wires":[]},{"id":"dac87e40.90376","type":"inject","z":"e6956267.5d174","name":"","props":[{"p":"payload","v":"Hello, World!","vt":"str"},{"p":"topic","v":"Sample","vt":"string"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Sample","payload":"Hello, World!","payloadType":"str","x":220,"y":100,"wires":[["a77fa5e3.fac248"]]},{"id":"a77fa5e3.fac248","type":"debug","z":"e6956267.5d174","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":410,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json b/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json index a5866a723..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json @@ -1 +0,0 @@ -[{"id":"fb1c3ce9.c29ee","type":"comment","z":"395f4b0d.8a8774","name":"Output to console","info":"Debug node can be used to output values to console.","x":130,"y":60,"wires":[]},{"id":"3c24e746.9ff6a8","type":"inject","z":"395f4b0d.8a8774","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":170,"y":100,"wires":[["66cc7b44.82ba74"]]},{"id":"66cc7b44.82ba74","type":"debug","z":"395f4b0d.8a8774","name":"","active":true,"tosidebar":false,"console":true,"tostatus":false,"complete":"payload","targetType":"msg","x":420,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json b/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json index 9f1fb8265..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json @@ -1 +0,0 @@ -[{"id":"33791e6a.973502","type":"comment","z":"55587092.1f2b4","name":"Output to node status area","info":"Debug node can be used to output values to status area below the node.","x":170,"y":60,"wires":[]},{"id":"a5d8e744.a034e8","type":"inject","z":"55587092.1f2b4","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":190,"y":100,"wires":[["b0646a4d.db4bc8"]]},{"id":"b0646a4d.db4bc8","type":"debug","z":"55587092.1f2b4","name":"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":430,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json b/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json index 1fa64e5af..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json @@ -1 +0,0 @@ -[{"id":"30ed0a73.fcef86","type":"comment","z":"61feb619.5e3d68","name":"Formatting output using JSONata","info":"Debug node can format output value using JSONata expression.","x":200,"y":60,"wires":[]},{"id":"6f477e7d.3a8da","type":"inject","z":"61feb619.5e3d68","name":"","props":[{"p":"payload","v":"Hello, World!","vt":"str"},{"p":"topic","v":"Sample","vt":"string"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Sample","payload":"Hello, World!","payloadType":"str","x":220,"y":100,"wires":[["19c9408d.ac6d4f"]]},{"id":"19c9408d.ac6d4f","type":"debug","z":"61feb619.5e3d68","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"\"[\" & topic & \"] \" & payload","targetType":"jsonata","x":420,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json b/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json index e3304eb97..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json +++ b/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json @@ -1 +0,0 @@ -[{"id":"a30d20e6.ec6dc","type":"link in","z":"2f2d9fa4.be6fc","name":"","links":["70d6f012.8fe6d"],"x":235,"y":240,"wires":[["6bf52c5c.d301c4"]]},{"id":"70d6f012.8fe6d","type":"link out","z":"2f2d9fa4.be6fc","name":"","links":["a30d20e6.ec6dc"],"x":315,"y":180,"wires":[]},{"id":"353c85ce.993d0a","type":"inject","z":"2f2d9fa4.be6fc","name":"","topic":"","payload":"Hello, World!","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":180,"wires":[["70d6f012.8fe6d"]]},{"id":"6bf52c5c.d301c4","type":"debug","z":"2f2d9fa4.be6fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":370,"y":240,"wires":[]},{"id":"62ea32aa.d73aac","type":"comment","z":"2f2d9fa4.be6fc","name":"Example: Link Node","info":"Output of link out node can be connected to input of link in node. The connection between links in/out is not shown, so the flow representation can be simplified.","x":130,"y":40,"wires":[]},{"id":"85133fcc.e482","type":"comment","z":"2f2d9fa4.be6fc","name":"Link output of inject node to input of debug node","info":"","x":260,"y":100,"wires":[]},{"id":"c588bc36.87fec","type":"comment","z":"2f2d9fa4.be6fc","name":"↓ connect to link in node","info":"","x":410,"y":140,"wires":[]},{"id":"8abca900.6dfe78","type":"comment","z":"2f2d9fa4.be6fc","name":"↑ connect from link out node","info":"","x":340,"y":280,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json b/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json index e92ad38dc..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json @@ -1 +0,0 @@ -[{"id":"26e7643f.13ebcc","type":"comment","z":"f4cb1920.4d58c8","name":"Set any property value","info":"Change node can set value to any message property.","x":160,"y":60,"wires":[]},{"id":"4da2494d.9aff68","type":"inject","z":"f4cb1920.4d58c8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":120,"wires":[["5111e689.62e838"]]},{"id":"58ea5868.0596e8","type":"debug","z":"f4cb1920.4d58c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":530,"y":120,"wires":[]},{"id":"5111e689.62e838","type":"change","z":"f4cb1920.4d58c8","name":"set payload & topic","rules":[{"t":"set","p":"payload","pt":"msg","to":"Hello, World!","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Title","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":120,"wires":[["58ea5868.0596e8"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json b/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json index 2c251157a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json @@ -1 +0,0 @@ -[{"id":"a9039cda.3649e","type":"comment","z":"a808932c.4ca77","name":"Set value using JSONata","info":"Change node can set value to using JSONata expression.","x":170,"y":60,"wires":[]},{"id":"bdcdd579.cfe668","type":"inject","z":"a808932c.4ca77","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":120,"wires":[["28d110a7.ce3e2"]]},{"id":"c6677fa5.8c111","type":"debug","z":"a808932c.4ca77","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":120,"wires":[]},{"id":"28d110a7.ce3e2","type":"change","z":"a808932c.4ca77","name":"use JSONata","rules":[{"t":"set","p":"payload","pt":"msg","to":"Hello","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"payload & \", World!\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":120,"wires":[["c6677fa5.8c111"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json b/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json index 62f7cc509..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json @@ -1 +0,0 @@ -[{"id":"e9143349.a64f7","type":"comment","z":"a32e6d69.1b13b","name":"Set value from environment variable","info":"Change node can set value from environment variable.","x":200,"y":60,"wires":[]},{"id":"a7c2725.a631f9","type":"inject","z":"a32e6d69.1b13b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":120,"wires":[["e455c302.2f795"]]},{"id":"6f203119.21895","type":"debug","z":"a32e6d69.1b13b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":120,"wires":[]},{"id":"e455c302.2f795","type":"change","z":"a32e6d69.1b13b","name":"set env var","rules":[{"t":"set","p":"payload","pt":"msg","to":"HOME","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":120,"wires":[["6f203119.21895"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json b/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json index 8424b1d2b..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json @@ -1 +0,0 @@ -[{"id":"6ecac54d.c43ffc","type":"comment","z":"87ace6c0.f01da8","name":"Set flow context","info":"Change node can set flow context.","x":140,"y":60,"wires":[]},{"id":"80e966d3.9d7a78","type":"inject","z":"87ace6c0.f01da8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":234,"wires":[["abaee298.2d77e"]]},{"id":"60ab671d.b0bbf8","type":"debug","z":"87ace6c0.f01da8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":234,"wires":[]},{"id":"abaee298.2d77e","type":"change","z":"87ace6c0.f01da8","name":"increment count","rules":[{"t":"set","p":"count","pt":"flow","to":"$flowContext(\"count\")+1\t","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"count","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":234,"wires":[["60ab671d.b0bbf8"]]},{"id":"2de2bb38.f20ff4","type":"inject","z":"87ace6c0.f01da8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":140,"wires":[["7b96521e.a3cb0c"]]},{"id":"597b63cd.b3218c","type":"debug","z":"87ace6c0.f01da8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":140,"wires":[]},{"id":"7b96521e.a3cb0c","type":"change","z":"87ace6c0.f01da8","name":"set count to 0","rules":[{"t":"set","p":"count","pt":"flow","to":"0","tot":"num"},{"t":"set","p":"payload","pt":"msg","to":"count","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":140,"wires":[["597b63cd.b3218c"]]},{"id":"3d8cdc6d.2620e4","type":"comment","z":"87ace6c0.f01da8","name":"↓ Initialize","info":"","x":200,"y":100,"wires":[]},{"id":"d8069121.80de7","type":"comment","z":"87ace6c0.f01da8","name":"↓ Count up","info":"","x":200,"y":194,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json b/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json index 849c6d9b4..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json @@ -1 +0,0 @@ -[{"id":"87bd706a.aec93","type":"comment","z":"3ae4b3d9.1f77bc","name":"Delay message","info":"Delay node can delay sending input message to output port by a specified amount of time.","x":160,"y":60,"wires":[]},{"id":"1d17715c.34170f","type":"inject","z":"3ae4b3d9.1f77bc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":210,"y":120,"wires":[["26b43de5.4df8f2","9930fecd.ee0c8"]]},{"id":"9930fecd.ee0c8","type":"debug","z":"3ae4b3d9.1f77bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":410,"y":120,"wires":[]},{"id":"26b43de5.4df8f2","type":"delay","z":"3ae4b3d9.1f77bc","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":400,"y":180,"wires":[["c8c2796c.dcb9f8"]]},{"id":"c8c2796c.dcb9f8","type":"change","z":"3ae4b3d9.1f77bc","name":"Goodbye, World!","rules":[{"t":"set","p":"payload","pt":"msg","to":"Goodbye, World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":180,"wires":[["c58a290e.2fa438"]]},{"id":"c58a290e.2fa438","type":"debug","z":"3ae4b3d9.1f77bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":790,"y":180,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json b/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json index 9b11ad0d3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json @@ -1 +0,0 @@ -[{"id":"c15b8c3e.955ed","type":"comment","z":"6f1773ed.b7c2fc","name":"Delay message by message property","info":"Delay node can delay sending input message to output port by a specified amount of time by `msg.delay` property.","x":210,"y":60,"wires":[]},{"id":"a5ed5817.9df448","type":"inject","z":"6f1773ed.b7c2fc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"delay","v":"1000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"delay 1s","payloadType":"str","x":180,"y":120,"wires":[["5cf53f4.25b7ec","59b7b67a.a8e888"]]},{"id":"59b7b67a.a8e888","type":"debug","z":"6f1773ed.b7c2fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":390,"y":120,"wires":[]},{"id":"5cf53f4.25b7ec","type":"delay","z":"6f1773ed.b7c2fc","name":"","pauseType":"delayv","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":380,"y":180,"wires":[["fc989f41.c4114"]]},{"id":"fc989f41.c4114","type":"change","z":"6f1773ed.b7c2fc","name":"Goodbye, World!","rules":[{"t":"set","p":"payload","pt":"msg","to":"Goodbye, World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":180,"wires":[["74ba3d1c.666034"]]},{"id":"74ba3d1c.666034","type":"debug","z":"6f1773ed.b7c2fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":770,"y":180,"wires":[]},{"id":"6cdf7297.bf5a8c","type":"inject","z":"6f1773ed.b7c2fc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"delay","v":"10000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"delay 10s","payloadType":"str","x":180,"y":180,"wires":[["59b7b67a.a8e888","5cf53f4.25b7ec"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json b/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json index 0a4e82e50..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json @@ -1 +0,0 @@ -[{"id":"b0200f61.5efa5","type":"comment","z":"86a4fcf3.9f442","name":"Reset or flush pending message","info":"Delay node can reset or flush delayed message by sending it a message with `reset` or `flush` property.","x":170,"y":60,"wires":[]},{"id":"d5cd8991.e6d2e8","type":"inject","z":"86a4fcf3.9f442","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":170,"y":120,"wires":[["607f556b.3ec5fc","fd14cb.2044db38"]]},{"id":"fd14cb.2044db38","type":"debug","z":"86a4fcf3.9f442","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":370,"y":120,"wires":[]},{"id":"607f556b.3ec5fc","type":"delay","z":"86a4fcf3.9f442","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":360,"y":180,"wires":[["d1fc6763.2a30c8"]]},{"id":"d1fc6763.2a30c8","type":"change","z":"86a4fcf3.9f442","name":"Goodbye, World!","rules":[{"t":"set","p":"payload","pt":"msg","to":"Goodbye, World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":180,"wires":[["15486d4a.80c6f3"]]},{"id":"15486d4a.80c6f3","type":"debug","z":"86a4fcf3.9f442","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":750,"y":180,"wires":[]},{"id":"2b8b28c7.4c8978","type":"inject","z":"86a4fcf3.9f442","name":"reset","props":[{"p":"topic","vt":"str"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":180,"wires":[["607f556b.3ec5fc"]]},{"id":"3a7e1bec.8bc3d4","type":"inject","z":"86a4fcf3.9f442","name":"flush","props":[{"p":"topic","vt":"str"},{"p":"flush","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":240,"wires":[["607f556b.3ec5fc"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json b/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json index 8d6bc7518..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json @@ -1 +0,0 @@ -[{"id":"3dc5015b.96c97e","type":"comment","z":"73c00795.a13908","name":"Limit rate of message transfer for each topic","info":"Delay node can limit of message transmission from input to output port by a specified number of message per a specified time.\nIf `For each topic` is selected, messages are grouped by `msg.topic` value. When grouping messages by topic, intermediate messages are dropped and the last messages received sent.","x":210,"y":60,"wires":[]},{"id":"bdafe4c6.4d5658","type":"inject","z":"73c00795.a13908","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"topic\":\"apple\",\"payload\":1},{\"topic\":\"apple\",\"payload\":2},{\"topic\":\"apple\",\"payload\":3},{\"topic\":\"orange\",\"payload\":1},{\"topic\":\"orange\",\"payload\":2},{\"topic\":\"orange\",\"payload\":3},{\"topic\":\"banana\",\"payload\":1},{\"topic\":\"banana\",\"payload\":2},{\"topic\":\"banana\",\"payload\":3}]","payloadType":"json","x":150,"y":120,"wires":[["f86dc462.195818"]]},{"id":"e0bdfcc1.cdf48","type":"delay","z":"73c00795.a13908","name":"","pauseType":"timed","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":690,"y":120,"wires":[["29d8beea.6b37f2"]]},{"id":"29d8beea.6b37f2","type":"debug","z":"73c00795.a13908","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":890,"y":120,"wires":[]},{"id":"f86dc462.195818","type":"split","z":"73c00795.a13908","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":290,"y":120,"wires":[["9feb3aac.616c38"]]},{"id":"9feb3aac.616c38","type":"change","z":"73c00795.a13908","name":"set topic&payload","rules":[{"t":"set","p":"topic","pt":"msg","to":"payload.topic","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload.payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":120,"wires":[["e0bdfcc1.cdf48"]]},{"id":"949199e8.89bc88","type":"comment","z":"73c00795.a13908","name":"↑ pass last message of each topic","info":"","x":740,"y":160,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json b/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json index 365e4405c..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json +++ b/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json @@ -1 +0,0 @@ -[{"id":"6a5f26a9.0cc2d8","type":"inject","z":"835cc8cc.b8cca8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World!","payloadType":"str","x":190,"y":160,"wires":[["fc2b343c.bbe2f8"]]},{"id":"fc2b343c.bbe2f8","type":"exec","z":"835cc8cc.b8cca8","command":"echo","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":330,"y":160,"wires":[["2f3bcd73.6fedf2"],[],["3280586e.4e3d28"]]},{"id":"2f3bcd73.6fedf2","type":"debug","z":"835cc8cc.b8cca8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":510,"y":160,"wires":[]},{"id":"2b7bc9f1.ab36a6","type":"comment","z":"835cc8cc.b8cca8","name":"Execute external command appending additional args","info":"Exec node can execute external command and can receive its standard output as a payload of first message. Standard error output can be received from second message. The exit code of the command can be obtained from `code` property of third message payload.\n\nIf `Append msg.payload` checkbox is selected, payload value of the input message is appended to command string.\n","x":260,"y":60,"wires":[]},{"id":"3280586e.4e3d28","type":"debug","z":"835cc8cc.b8cca8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":510,"y":220,"wires":[]},{"id":"feba83da.8f227","type":"comment","z":"835cc8cc.b8cca8","name":"↓ execute echo command","info":"","x":390,"y":115,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json b/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json index 992b39df3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json +++ b/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json @@ -1 +0,0 @@ -[{"id":"f507b27c.fff1","type":"inject","z":"462f83a6.d3c3cc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":180,"wires":[["28b4f75a.eae548"]]},{"id":"28b4f75a.eae548","type":"exec","z":"462f83a6.d3c3cc","command":"/non/existing/command","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":350,"y":180,"wires":[[],["27992c1f.a1c964"],["6e7ff001.2412d"]]},{"id":"6e7ff001.2412d","type":"debug","z":"462f83a6.d3c3cc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":240,"wires":[]},{"id":"27992c1f.a1c964","type":"debug","z":"462f83a6.d3c3cc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":180,"wires":[]},{"id":"77e85c7c.a560d4","type":"comment","z":"462f83a6.d3c3cc","name":"Execute external command and get error output","info":"Exec node can execute external command and can receive its standard output as a payload of first message. Standard error output can be received from second message. The exit code of the command can be obtained from `code` property of third message payload.\n","x":220,"y":80,"wires":[]},{"id":"c188c28c.f7d34","type":"comment","z":"462f83a6.d3c3cc","name":"↓ try to execute non-existing command","info":"","x":390,"y":134,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json b/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json index f45eb6991..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json +++ b/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json @@ -1 +0,0 @@ -[{"id":"990f33d3.3ffe2","type":"comment","z":"db89ae0c.f52b5","name":"Scale input value & round result to integer","info":"Range node can map input value to output value according to mapping specification.\nThe result value is rounded to nearest integer if `Round result to the nearest integer?` checkbox is checked.","x":240,"y":60,"wires":[]},{"id":"b2639501.5ad638","type":"inject","z":"db89ae0c.f52b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":190,"y":120,"wires":[["dc4509ee.773038"]]},{"id":"dc4509ee.773038","type":"range","z":"db89ae0c.f52b5","minin":"0","maxin":"9","minout":"0","maxout":"128","action":"scale","round":true,"property":"payload","name":"","x":360,"y":120,"wires":[["50991e7e.2ed24"]]},{"id":"50991e7e.2ed24","type":"debug","z":"db89ae0c.f52b5","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":520,"y":120,"wires":[]},{"id":"335e877f.d24e98","type":"inject","z":"db89ae0c.f52b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":190,"y":160,"wires":[["dc4509ee.773038"]]},{"id":"5d88207d.4189","type":"inject","z":"db89ae0c.f52b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"99","payloadType":"num","x":190,"y":200,"wires":[["dc4509ee.773038"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json b/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json index c637c86f9..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json +++ b/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json @@ -1 +0,0 @@ -[{"id":"f0bc6a80.d52578","type":"comment","z":"bdb79f11.23e1d","name":"Limit input value","info":"Range node can map input value to output value according to mapping specification.\nThe result value is limited to specified range if `Scale and limit to the target range` is selected.","x":140,"y":60,"wires":[]},{"id":"69652849.749198","type":"inject","z":"bdb79f11.23e1d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":120,"wires":[["104f6f14.1c72e1"]]},{"id":"104f6f14.1c72e1","type":"range","z":"bdb79f11.23e1d","minin":"0","maxin":"100","minout":"0","maxout":"90","action":"clamp","round":false,"property":"payload","name":"","x":330,"y":120,"wires":[["10fcbed4.9c8d71"]]},{"id":"10fcbed4.9c8d71","type":"debug","z":"bdb79f11.23e1d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":120,"wires":[]},{"id":"93bb4526.7d6e28","type":"inject","z":"bdb79f11.23e1d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":170,"y":160,"wires":[["104f6f14.1c72e1"]]},{"id":"6892dfd8.42386","type":"inject","z":"bdb79f11.23e1d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"num","x":170,"y":200,"wires":[["104f6f14.1c72e1"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json b/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json index ce8cb733a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json +++ b/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json @@ -1 +0,0 @@ -[{"id":"808e354d.8de148","type":"comment","z":"b1446352.d689e","name":"Scale and wrap input value","info":"Range node can map input value to output value according to mapping specification.\nThe result value is wrapped if `Scale and wrap within the target range` is selected.","x":170,"y":60,"wires":[]},{"id":"2f033c72.51dcc4","type":"inject","z":"b1446352.d689e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":120,"wires":[["cdaa82c4.820de"]]},{"id":"cdaa82c4.820de","type":"range","z":"b1446352.d689e","minin":"0","maxin":"9","minout":"0","maxout":"90","action":"roll","round":false,"property":"payload","name":"","x":330,"y":120,"wires":[["77f8872b.2a9968"]]},{"id":"77f8872b.2a9968","type":"debug","z":"b1446352.d689e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":120,"wires":[]},{"id":"84ac6a7e.77b8d8","type":"inject","z":"b1446352.d689e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":170,"y":160,"wires":[["cdaa82c4.820de"]]},{"id":"92f554c3.b08ad8","type":"inject","z":"b1446352.d689e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"num","x":170,"y":200,"wires":[["cdaa82c4.820de"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json b/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json index 8a51a99f1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json @@ -1 +0,0 @@ -[{"id":"6ec19fc7.a32ae","type":"comment","z":"e482b0ab.b1b43","name":"Check all rules","info":"Switch node apply all rules if `checking all rules` is selected in settings panel.","x":120,"y":60,"wires":[]},{"id":"1644e138.f8d1ef","type":"switch","z":"e482b0ab.b1b43","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lt","v":"10","vt":"num"},{"t":"gt","v":"-10","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":290,"y":140,"wires":[["624b4e9f.37fee"],["a89d6432.b68318"]]},{"id":"a7f64dbf.3e27b","type":"debug","z":"e482b0ab.b1b43","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":100,"wires":[]},{"id":"a89d6432.b68318","type":"change","z":"e482b0ab.b1b43","name":">-10","rules":[{"t":"set","p":"payload","pt":"msg","to":">-10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":180,"wires":[["f1136da2.23516"]]},{"id":"624b4e9f.37fee","type":"change","z":"e482b0ab.b1b43","name":"<10","rules":[{"t":"set","p":"payload","pt":"msg","to":"<10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":100,"wires":[["a7f64dbf.3e27b"]]},{"id":"f1136da2.23516","type":"debug","z":"e482b0ab.b1b43","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":180,"wires":[]},{"id":"c7e952e9.88e5e","type":"inject","z":"e482b0ab.b1b43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"-10","payloadType":"num","x":150,"y":100,"wires":[["1644e138.f8d1ef"]]},{"id":"8cf8babd.b43db8","type":"inject","z":"e482b0ab.b1b43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":150,"y":180,"wires":[["1644e138.f8d1ef"]]},{"id":"6a43ae86.b92ed","type":"inject","z":"e482b0ab.b1b43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":150,"y":140,"wires":[["1644e138.f8d1ef"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json b/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json index 0b41e75e3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json @@ -1 +0,0 @@ -[{"id":"a12a5708.195688","type":"comment","z":"74b5d6de.372b18","name":"Stop after first match","info":"Switch node stops application of rules if `stopping after first match` is selected in settings panel and a rule evaluates to `true`.","x":160,"y":60,"wires":[]},{"id":"8aebdebf.5d7f2","type":"switch","z":"74b5d6de.372b18","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lt","v":"10","vt":"num"},{"t":"gt","v":"-10","vt":"num"}],"checkall":"false","repair":false,"outputs":2,"x":310,"y":140,"wires":[["5d1851c.a9c5db"],["e8228605.f32018"]]},{"id":"60248c98.69fd44","type":"debug","z":"74b5d6de.372b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":100,"wires":[]},{"id":"e8228605.f32018","type":"change","z":"74b5d6de.372b18","name":">-10","rules":[{"t":"set","p":"payload","pt":"msg","to":">-10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":180,"wires":[["e9a51608.c322b8"]]},{"id":"5d1851c.a9c5db","type":"change","z":"74b5d6de.372b18","name":"<10","rules":[{"t":"set","p":"payload","pt":"msg","to":"<10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":100,"wires":[["60248c98.69fd44"]]},{"id":"e9a51608.c322b8","type":"debug","z":"74b5d6de.372b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":180,"wires":[]},{"id":"49222b4b.647a84","type":"inject","z":"74b5d6de.372b18","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"-10","payloadType":"num","x":170,"y":100,"wires":[["8aebdebf.5d7f2"]]},{"id":"39e8c133.a56f7e","type":"inject","z":"74b5d6de.372b18","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":170,"y":180,"wires":[["8aebdebf.5d7f2"]]},{"id":"3a22ec96.965a14","type":"inject","z":"74b5d6de.372b18","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":140,"wires":[["8aebdebf.5d7f2"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json b/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json index b5e386fa1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json @@ -1 +0,0 @@ -[{"id":"6dfd4381.eae2ec","type":"comment","z":"7d002985.82f928","name":"Select output based on type of payload value","info":"Switch node can route input message based on type of payload value.","x":210,"y":40,"wires":[]},{"id":"b139dacf.a0d818","type":"switch","z":"7d002985.82f928","name":"","property":"payload","propertyType":"msg","rules":[{"t":"istype","v":"string","vt":"string"},{"t":"istype","v":"number","vt":"number"}],"checkall":"false","repair":false,"outputs":2,"x":330,"y":120,"wires":[["7c5cd60c.e66b28"],["86cf5262.20f18"]]},{"id":"ca403f12.477a8","type":"debug","z":"7d002985.82f928","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":160,"wires":[]},{"id":"7c5cd60c.e66b28","type":"change","z":"7d002985.82f928","name":"String","rules":[{"t":"set","p":"payload","pt":"msg","to":"string","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":80,"wires":[["d9669648.1177e8"]]},{"id":"86cf5262.20f18","type":"change","z":"7d002985.82f928","name":"Number","rules":[{"t":"set","p":"payload","pt":"msg","to":"number","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":160,"wires":[["ca403f12.477a8"]]},{"id":"d9669648.1177e8","type":"debug","z":"7d002985.82f928","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":80,"wires":[]},{"id":"d3f1c718.dc67e8","type":"inject","z":"7d002985.82f928","name":"Number:128","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"128","payloadType":"num","x":170,"y":80,"wires":[["b139dacf.a0d818"]]},{"id":"a59e275e.6d48c8","type":"inject","z":"7d002985.82f928","name":"String:128","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"128","payloadType":"str","x":160,"y":160,"wires":[["b139dacf.a0d818"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json b/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json index f0955a3f1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json @@ -1 +0,0 @@ -[{"id":"175ceb0d.8dbe45","type":"comment","z":"17f634f4.e4bc9b","name":"Use JSONata expression for rules","info":"Switch node can use JSONata expression for calculating complex conditions.","x":200,"y":60,"wires":[]},{"id":"d89491c3.793a3","type":"switch","z":"17f634f4.e4bc9b","name":"","property":"payload","propertyType":"msg","rules":[{"t":"jsonata_exp","v":"(payload % 2) = 0","vt":"jsonata"},{"t":"jsonata_exp","v":"(payload % 2) = 1","vt":"jsonata"}],"checkall":"false","repair":false,"outputs":2,"x":310,"y":140,"wires":[["d6cb78a6.872908"],["1f0c62bb.c3d52d"]]},{"id":"9ae9a2aa.a895c","type":"debug","z":"17f634f4.e4bc9b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":100,"wires":[]},{"id":"1f0c62bb.c3d52d","type":"change","z":"17f634f4.e4bc9b","name":"Odd","rules":[{"t":"set","p":"payload","pt":"msg","to":"odd","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":180,"wires":[["34b0a9fb.bafdb6"]]},{"id":"d6cb78a6.872908","type":"change","z":"17f634f4.e4bc9b","name":"Even","rules":[{"t":"set","p":"payload","pt":"msg","to":"even","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":100,"wires":[["9ae9a2aa.a895c"]]},{"id":"34b0a9fb.bafdb6","type":"debug","z":"17f634f4.e4bc9b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":180,"wires":[]},{"id":"7beb0333.a55bac","type":"inject","z":"17f634f4.e4bc9b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"7","payloadType":"num","x":170,"y":100,"wires":[["d89491c3.793a3"]]},{"id":"a8db47cf.58ba18","type":"inject","z":"17f634f4.e4bc9b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"8","payloadType":"num","x":170,"y":180,"wires":[["d89491c3.793a3"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json b/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json index 3404b4ff9..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json @@ -1 +0,0 @@ -[{"id":"1c09dc33.934504","type":"comment","z":"166069bc.648516","name":"Use JSONata expression for switch value","info":"Switch node can use JSONata expression for calculating complex switch value.","x":200,"y":60,"wires":[]},{"id":"c7ca4974.d638f8","type":"switch","z":"166069bc.648516","name":"","property":"(payload % 2) = 0","propertyType":"jsonata","rules":[{"t":"true"},{"t":"false"}],"checkall":"false","repair":false,"outputs":2,"x":290,"y":140,"wires":[["ac921b1d.c0dbe8"],["89adcfcc.53d6d"]]},{"id":"ab8972e0.e98b7","type":"debug","z":"166069bc.648516","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":100,"wires":[]},{"id":"89adcfcc.53d6d","type":"change","z":"166069bc.648516","name":"Odd","rules":[{"t":"set","p":"payload","pt":"msg","to":"odd","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":180,"wires":[["426c68cf.64f0e8"]]},{"id":"ac921b1d.c0dbe8","type":"change","z":"166069bc.648516","name":"Even","rules":[{"t":"set","p":"payload","pt":"msg","to":"even","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":100,"wires":[["ab8972e0.e98b7"]]},{"id":"426c68cf.64f0e8","type":"debug","z":"166069bc.648516","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":180,"wires":[]},{"id":"63377f1c.2fbfc","type":"inject","z":"166069bc.648516","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"7","payloadType":"num","x":150,"y":100,"wires":[["c7ca4974.d638f8"]]},{"id":"ea2fa596.ff1638","type":"inject","z":"166069bc.648516","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"8","payloadType":"num","x":150,"y":180,"wires":[["c7ca4974.d638f8"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json b/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json index de123bbe8..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json +++ b/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json @@ -1 +0,0 @@ -[{"id":"eaf91a6b.a55da8","type":"comment","z":"73a69428.bf4fec","name":"Advanced mustache example","info":"Template node can create a string value using [Mustache](http://mustache.github.io/mustache.5.html) syntax.","x":200,"y":80,"wires":[]},{"id":"61fbfe34.14a02","type":"inject","z":"73a69428.bf4fec","name":"Price of fruits","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Fruits","payload":"[{\"name\":\"apple\",\"price\":100},{\"name\":\"orange\",\"price\":80},{\"name\":\"banana\",\"price\":210}]","payloadType":"json","x":210,"y":140,"wires":[["bf0cb02.d8e4b5"]]},{"id":"bf0cb02.d8e4b5","type":"template","z":"73a69428.bf4fec","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"# Price List of {{topic}}\n\n{{! outputs list of prices }}\n{{#payload}}\n- {{name}}: {{price}}\n{{/payload}}\n","output":"str","x":380,"y":140,"wires":[["153eb0ff.5622df"]]},{"id":"153eb0ff.5622df","type":"debug","z":"73a69428.bf4fec","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":550,"y":140,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json b/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json index d2042aa98..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json +++ b/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json @@ -1 +0,0 @@ -[{"id":"fe821493.2e0e28","type":"comment","z":"1e6bd604.afc8fa","name":"Parse result as JSON","info":"Template node can create a string value using [Mustache](http://mustache.github.io/mustache.5.html) syntax.\nIf `Partsed JSON` output is selected, the created string is parsed as JSON format and JavaScript object is send as an output payload value.","x":160,"y":60,"wires":[]},{"id":"931f94e8.592cd8","type":"inject","z":"1e6bd604.afc8fa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"message","payload":"Hello, World!","payloadType":"str","x":220,"y":120,"wires":[["bb2b0dad.b24b5"]]},{"id":"bb2b0dad.b24b5","type":"template","z":"1e6bd604.afc8fa","name":"JSON template","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"{\n \"key\" : \"{{topic}}\",\n \"value\": \"{{payload}}\"\n}\n","output":"json","x":440,"y":120,"wires":[["baf2e48.2b97418"]]},{"id":"baf2e48.2b97418","type":"debug","z":"1e6bd604.afc8fa","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":120,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json b/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json index dc01b1e3a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json +++ b/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json @@ -1 +0,0 @@ -[{"id":"6ad06659.a1e4e8","type":"comment","z":"369312e8.ba755e","name":"Parse result as YAML","info":"Template node can create a string value using [Mustache](http://mustache.github.io/mustache.5.html) syntax.\nIf `Partsed YAML` output is selected, the created string is parsed as YAML format and JavaScript object is send as an output payload value.","x":180,"y":60,"wires":[]},{"id":"8d6be9a2.c3fa58","type":"inject","z":"369312e8.ba755e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"message","payload":"Hello, World!","payloadType":"str","x":240,"y":120,"wires":[["69369c3.4a98164"]]},{"id":"69369c3.4a98164","type":"template","z":"369312e8.ba755e","name":"YAML template","field":"payload","fieldType":"msg","format":"yaml","syntax":"mustache","template":"key: {{topic}}\nvalue: {{payload}}","output":"yaml","x":460,"y":120,"wires":[["11fb2934.f5de27"]]},{"id":"11fb2934.f5de27","type":"debug","z":"369312e8.ba755e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":120,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json b/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json index 401e6c681..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json +++ b/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json @@ -1 +0,0 @@ -[{"id":"ec5a531b.68b65","type":"inject","z":"90acd374.2feda","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":100,"wires":[["cb5e0c78.4bf3d"]]},{"id":"1b0f8c3e.1fd7e4","type":"debug","z":"90acd374.2feda","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":490,"y":100,"wires":[]},{"id":"cb5e0c78.4bf3d","type":"trigger","z":"90acd374.2feda","name":"","op1":"1","op2":"0","op1type":"str","op2type":"str","duration":"2","extend":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":320,"y":100,"wires":[["1b0f8c3e.1fd7e4"]]},{"id":"4e5bf6b2.b4dd58","type":"comment","z":"90acd374.2feda","name":"Oputputs two values with interval","info":"Outputs 1. Then output 0 after a certain period of time.\n\n*This could be used, for example, to blink an LED attached to a Raspberry Pi GPIO pin.*","x":170,"y":40,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json b/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json index 0a60fa95d..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json +++ b/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json @@ -1,135 +0,0 @@ -[ - { - "id": "9b2d7459.8dd598", - "type": "http in", - "z": "d41b4dd3.ecd6a", - "name": "", - "url": "/hello-param/:name", - "method": "get", - "upload": false, - "swaggerDoc": "", - "x": 290, - "y": 900, - "wires": [ - [ - "83753c80.5e271" - ] - ] - }, - { - "id": "7fe50f46.46209", - "type": "http response", - "z": "d41b4dd3.ecd6a", - "name": "", - "statusCode": "", - "headers": {}, - "x": 590, - "y": 900, - "wires": [] - }, - { - "id": "6e88d2b.828a92c", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Handle URL parameters in an HTTP endpoint", - "info": "Named path parameters (e.g. `:name`) in the URL property can be used to identify parts of the path that can vary between requests.\n\nThe `msg.req.params` property is an object of key/value pairs for each path parameter.", - "x": 290, - "y": 860, - "wires": [] - }, - { - "id": "214bc398.b3482c", - "type": "http request", - "z": "d41b4dd3.ecd6a", - "name": "", - "method": "GET", - "ret": "txt", - "paytoqs": "ignore", - "url": "http://localhost:1880/hello-param/Nick", - "tls": "", - "persist": false, - "proxy": "", - "authType": "", - "x": 390, - "y": 1000, - "wires": [ - [ - "70c0eba4.5f0dc4" - ] - ] - }, - { - "id": "70c0eba4.5f0dc4", - "type": "debug", - "z": "d41b4dd3.ecd6a", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 550, - "y": 1000, - "wires": [] - }, - { - "id": "83753c80.5e271", - "type": "template", - "z": "d41b4dd3.ecd6a", - "name": "page", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "mustache", - "template": "\n \n \n

Hello {{req.params.name}}!

\n \n", - "output": "str", - "x": 470, - "y": 900, - "wires": [ - [ - "7fe50f46.46209" - ] - ] - }, - { - "id": "89523bfe.6e5c18", - "type": "inject", - "z": "d41b4dd3.ecd6a", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 240, - "y": 1000, - "wires": [ - [ - "214bc398.b3482c" - ] - ] - }, - { - "id": "6276a6cd.5c3b18", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Send HTTP GET request: http://localhost:1880/hello-param/Nick", - "info": "`http request` node can be used to send **HTTP GET** request.", - "x": 350, - "y": 960, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json b/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json index aec360a91..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json +++ b/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json @@ -1,126 +0,0 @@ -[ - { - "id": "59760c9d.172d94", - "type": "http in", - "z": "d41b4dd3.ecd6a", - "name": "", - "url": "/hello-upload", - "method": "post", - "upload": true, - "swaggerDoc": "", - "x": 270, - "y": 1980, - "wires": [ - [ - "e30073f3.ad429" - ] - ] - }, - { - "id": "d0b3e8b4.2cfde8", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Post file to a flow", - "info": "The `HTTP In` node can listen for POST requests for file upload. Information for uploaded files can be accessed from `msg.req.files`.\n", - "x": 200, - "y": 1760, - "wires": [] - }, - { - "id": "a888a043.ebb72", - "type": "http in", - "z": "d41b4dd3.ecd6a", - "name": "", - "url": "/hello-upload", - "method": "get", - "upload": false, - "swaggerDoc": "", - "x": 270, - "y": 1880, - "wires": [ - [ - "14b950b.0ed5aaf" - ] - ] - }, - { - "id": "2ccb67c7.02c568", - "type": "http response", - "z": "d41b4dd3.ecd6a", - "name": "", - "statusCode": "", - "headers": {}, - "x": 590, - "y": 1880, - "wires": [] - }, - { - "id": "14b950b.0ed5aaf", - "type": "template", - "z": "d41b4dd3.ecd6a", - "name": "", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "mustache", - "template": "\n \n \n
\n
\n \n \n
\n
\n \n
\n
\n \n", - "output": "str", - "x": 440, - "y": 1880, - "wires": [ - [ - "2ccb67c7.02c568" - ] - ] - }, - { - "id": "54f2edb5.62fca4", - "type": "http response", - "z": "d41b4dd3.ecd6a", - "name": "", - "statusCode": "", - "headers": {}, - "x": 590, - "y": 1980, - "wires": [] - }, - { - "id": "e30073f3.ad429", - "type": "template", - "z": "d41b4dd3.ecd6a", - "name": "", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "mustache", - "template": "\n \n \n

Hello {{req.files.0.originalname}}

\n \n", - "output": "str", - "x": 440, - "y": 1980, - "wires": [ - [ - "54f2edb5.62fca4" - ] - ] - }, - { - "id": "6c3c1bf.48cc2e4", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Create HTTP page for file upload: open http://localhost:1880/hello-upload \\n from Web browser. Then upload a file.", - "info": "", - "x": 440, - "y": 1820, - "wires": [] - }, - { - "id": "c6242caa.8ec63", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Serve POST request for file upload", - "info": "", - "x": 320, - "y": 1940, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json b/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json index 78c24760c..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json @@ -1,92 +0,0 @@ -[ - { - "id": "73874b9e.d985b4", - "type": "tcp in", - "z": "cfd9159c.6f73e8", - "name": "", - "server": "client", - "host": "localhost", - "port": "1881", - "datamode": "single", - "datatype": "utf8", - "newline": "", - "topic": "", - "base64": false, - "x": 230, - "y": 240, - "wires": [ - [ - "ce332113.d2c31" - ] - ] - }, - { - "id": "a4bd9948.dfeb58", - "type": "tcp out", - "z": "cfd9159c.6f73e8", - "host": "localhost", - "port": "1881", - "beserver": "server", - "base64": false, - "end": true, - "name": "", - "x": 390, - "y": 180, - "wires": [] - }, - { - "id": "9f80f9c7.1e3c98", - "type": "inject", - "z": "cfd9159c.6f73e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 210, - "y": 180, - "wires": [ - [ - "a4bd9948.dfeb58" - ] - ] - }, - { - "id": "ce332113.d2c31", - "type": "debug", - "z": "cfd9159c.6f73e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 410, - "y": 240, - "wires": [] - }, - { - "id": "711ed10d.1d765", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "Connect to TCP out server", - "info": "`TCP in` node can connect to network server using tcp protocol. `TCP out` node can serve a network server using tcp procol.\n", - "x": 190, - "y": 120, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json b/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json index 357cbb9ca..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json @@ -1,92 +0,0 @@ -[ - { - "id": "511f8208.c4c20c", - "type": "tcp in", - "z": "cfd9159c.6f73e8", - "name": "", - "server": "server", - "host": "localhost", - "port": "1882", - "datamode": "single", - "datatype": "utf8", - "newline": "", - "topic": "", - "base64": false, - "x": 230, - "y": 460, - "wires": [ - [ - "6b8be121.32be9" - ] - ] - }, - { - "id": "ec0bc4aa.b3c828", - "type": "tcp out", - "z": "cfd9159c.6f73e8", - "host": "localhost", - "port": "1882", - "beserver": "client", - "base64": false, - "end": true, - "name": "", - "x": 390, - "y": 400, - "wires": [] - }, - { - "id": "17cf7d56.9efb03", - "type": "inject", - "z": "cfd9159c.6f73e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 210, - "y": 400, - "wires": [ - [ - "ec0bc4aa.b3c828" - ] - ] - }, - { - "id": "6b8be121.32be9", - "type": "debug", - "z": "cfd9159c.6f73e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 410, - "y": 460, - "wires": [] - }, - { - "id": "76196665.7c23e8", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "Connect to TCP in server", - "info": "`TCP out` node can connect to network server using tcp protocol. `TCP in` node can serve a network server using tcp procol.\n", - "x": 190, - "y": 340, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json b/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json index b4a324261..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json +++ b/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json @@ -1,174 +0,0 @@ -[ - { - "id": "d9a91d7e.05f0d", - "type": "tcp in", - "z": "cfd9159c.6f73e8", - "name": "", - "server": "server", - "host": "localhost", - "port": "1883", - "datamode": "stream", - "datatype": "utf8", - "newline": "¥n", - "topic": "", - "base64": false, - "x": 230, - "y": 740, - "wires": [ - [ - "3871d8af.25e208" - ] - ] - }, - { - "id": "9fa8f09d.7591b", - "type": "inject", - "z": "cfd9159c.6f73e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "\"Hello, World!¥n\"", - "payloadType": "jsonata", - "x": 190, - "y": 640, - "wires": [ - [ - "948a8410.ab0a08" - ] - ] - }, - { - "id": "33df08b.753e9f8", - "type": "debug", - "z": "cfd9159c.6f73e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 710, - "y": 640, - "wires": [] - }, - { - "id": "948a8410.ab0a08", - "type": "tcp request", - "z": "cfd9159c.6f73e8", - "server": "localhost", - "port": "1883", - "out": "char", - "splitc": "\\n", - "name": "", - "x": 350, - "y": 640, - "wires": [ - [ - "f6d6be6a.efb4c" - ] - ] - }, - { - "id": "fbd442d8.cb273", - "type": "tcp out", - "z": "cfd9159c.6f73e8", - "host": "", - "port": "", - "beserver": "reply", - "base64": false, - "end": false, - "name": "", - "x": 530, - "y": 740, - "wires": [] - }, - { - "id": "3871d8af.25e208", - "type": "change", - "z": "cfd9159c.6f73e8", - "name": "set result", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "\"Received: \" & payload & \"\b\\n\"", - "tot": "jsonata" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 400, - "y": 740, - "wires": [ - [ - "fbd442d8.cb273" - ] - ] - }, - { - "id": "f6d6be6a.efb4c", - "type": "function", - "z": "cfd9159c.6f73e8", - "name": "Buffer to String", - "func": "msg.payload = msg.payload.toString();\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 540, - "y": 640, - "wires": [ - [ - "33df08b.753e9f8" - ] - ] - }, - { - "id": "e1412987.91dcc8", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "Send reply to client of TCP connection", - "info": "Input message from `TCP in` node may be passed to `TCP out` node to return a reply to client.\n", - "x": 230, - "y": 580, - "wires": [] - }, - { - "id": "5f43905f.2425d", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "↓ Accept request", - "info": "", - "x": 220, - "y": 700, - "wires": [] - }, - { - "id": "a6f57329.c87c6", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "↓ Reply result", - "info": "", - "x": 550, - "y": 700, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json b/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json index d53a05da8..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json +++ b/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json @@ -1,92 +0,0 @@ -[ - { - "id": "73279b5.5151664", - "type": "udp in", - "z": "92236e19.9e4cb", - "name": "", - "iface": "", - "port": "1881", - "ipv": "udp4", - "multicast": "false", - "group": "", - "datatype": "utf8", - "x": 220, - "y": 220, - "wires": [ - [ - "d98b60d3.7331e" - ] - ] - }, - { - "id": "fb19b98f.d5aa58", - "type": "udp out", - "z": "92236e19.9e4cb", - "name": "", - "addr": "localhost", - "iface": "", - "port": "1881", - "ipv": "udp4", - "outport": "", - "base64": false, - "multicast": "false", - "x": 410, - "y": 160, - "wires": [] - }, - { - "id": "33f18897.35d5b8", - "type": "inject", - "z": "92236e19.9e4cb", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 160, - "wires": [ - [ - "fb19b98f.d5aa58" - ] - ] - }, - { - "id": "d98b60d3.7331e", - "type": "debug", - "z": "92236e19.9e4cb", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 370, - "y": 220, - "wires": [] - }, - { - "id": "f86495aa.8a9848", - "type": "comment", - "z": "92236e19.9e4cb", - "name": "Transfer data using UDP protocol", - "info": "`UDP in` node can be used to receive data from `UDP out` node using UDP protocol.", - "x": 230, - "y": 100, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json b/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json index 5abc5c223..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json @@ -1,96 +0,0 @@ -[ - { - "id": "13410716.69c9d9", - "type": "websocket in", - "z": "a8360b47.074ec8", - "name": "", - "server": "89db22b6.9aa36", - "client": "", - "x": 280, - "y": 180, - "wires": [ - [ - "c2541f10.59544" - ] - ] - }, - { - "id": "c2541f10.59544", - "type": "debug", - "z": "a8360b47.074ec8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 430, - "y": 180, - "wires": [] - }, - { - "id": "6788839e.04576c", - "type": "inject", - "z": "a8360b47.074ec8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 290, - "y": 120, - "wires": [ - [ - "438c232a.06c2cc" - ] - ] - }, - { - "id": "438c232a.06c2cc", - "type": "websocket out", - "z": "a8360b47.074ec8", - "name": "", - "server": "", - "client": "63620788.bda128", - "x": 500, - "y": 120, - "wires": [] - }, - { - "id": "c88f97a9.4410f8", - "type": "comment", - "z": "a8360b47.074ec8", - "name": "Connect to websocket in server", - "info": "`websocket out` node can connect to web socket server. `websocket in` node can serve a web socket server.\n", - "x": 290, - "y": 80, - "wires": [] - }, - { - "id": "89db22b6.9aa36", - "type": "websocket-listener", - "path": "/ws1", - "wholemsg": "false" - }, - { - "id": "63620788.bda128", - "type": "websocket-client", - "path": "ws://localhost:1880/ws1", - "tls": "", - "wholemsg": "false" - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json b/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json index 667af1a95..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json @@ -1,96 +0,0 @@ -[ - { - "id": "759c0b2b.8a0484", - "type": "websocket in", - "z": "a8360b47.074ec8", - "name": "", - "server": "", - "client": "1d80bd86.93f372", - "x": 340, - "y": 520, - "wires": [ - [ - "1f7a7454.cb65ec" - ] - ] - }, - { - "id": "1f7a7454.cb65ec", - "type": "debug", - "z": "a8360b47.074ec8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 550, - "y": 520, - "wires": [] - }, - { - "id": "aa2fe781.e92b28", - "type": "inject", - "z": "a8360b47.074ec8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Goodbye, World!", - "payloadType": "str", - "x": 300, - "y": 460, - "wires": [ - [ - "f8bdbc9b.d82dd" - ] - ] - }, - { - "id": "f8bdbc9b.d82dd", - "type": "websocket out", - "z": "a8360b47.074ec8", - "name": "", - "server": "40bd4295.3e4ecc", - "client": "", - "x": 460, - "y": 460, - "wires": [] - }, - { - "id": "c3fbc602.2e7f08", - "type": "comment", - "z": "a8360b47.074ec8", - "name": "Connect to websocket out server", - "info": "`websocket out` node can connect to web socket server. `websocket in` node can serve a web socket server.\n", - "x": 290, - "y": 420, - "wires": [] - }, - { - "id": "1d80bd86.93f372", - "type": "websocket-client", - "path": "ws://localhost:1880/ws2", - "tls": "", - "wholemsg": "false" - }, - { - "id": "40bd4295.3e4ecc", - "type": "websocket-listener", - "path": "/ws2", - "wholemsg": "false" - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json index ed75294b1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json @@ -1,99 +0,0 @@ -[ - { - "id": "330f4888.cccb28", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 180, - "wires": [ - [ - "ed11f8d6.5e3c88" - ] - ] - }, - { - "id": "a0288b44.71d488", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": "", - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 180, - "wires": [ - [ - "369cbe42.4af9f2" - ] - ] - }, - { - "id": "ed11f8d6.5e3c88", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 180, - "wires": [ - [ - "a0288b44.71d488" - ] - ] - }, - { - "id": "369cbe42.4af9f2", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 180, - "wires": [] - }, - { - "id": "783cfaa6.52fbe4", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with default column name as messages", - "info": "CSV node can parse input CSV data.\nParsed CSV record can be sent as a message sequence.\nEach message payload points to an object with `col`*N* as a key and CSV value as a value.\n", - "x": 330, - "y": 120, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json index 85a03e486..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json @@ -1,99 +0,0 @@ -[ - { - "id": "98c9d44d.4457b8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 360, - "wires": [ - [ - "65476517.3d760c" - ] - ] - }, - { - "id": "76df98f7.0dcd08", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": "", - "hdrout": "none", - "multi": "mult", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 360, - "wires": [ - [ - "557979e0.e6b588" - ] - ] - }, - { - "id": "65476517.3d760c", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 360, - "wires": [ - [ - "76df98f7.0dcd08" - ] - ] - }, - { - "id": "557979e0.e6b588", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 360, - "wires": [] - }, - { - "id": "187f4ab3.4c9ab5", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with default column name as array", - "info": "CSV node can send a single message with array of parsed CSV records.\nEach element of the array consists of objects with key-value pair.", - "x": 320, - "y": 300, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json index 327a25313..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json @@ -1,99 +0,0 @@ -[ - { - "id": "1216e95b.1b1e87", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 560, - "wires": [ - [ - "e41ffbbc.de2ed8" - ] - ] - }, - { - "id": "286828bc.9233c8", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": "", - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "kind,price,origin", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 560, - "wires": [ - [ - "9d8218c.5550ee8" - ] - ] - }, - { - "id": "e41ffbbc.de2ed8", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 560, - "wires": [ - [ - "286828bc.9233c8" - ] - ] - }, - { - "id": "9d8218c.5550ee8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 560, - "wires": [] - }, - { - "id": "aaa1ee8f.21e2c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with specified column name as messages", - "info": "CSV node can specify column name of parsed objects in its settings panel.", - "x": 340, - "y": 500, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json index e287b57e7..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json @@ -1,99 +0,0 @@ -[ - { - "id": "24093558.0315aa", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 740, - "wires": [ - [ - "80abaee1.5fa7f" - ] - ] - }, - { - "id": "d4d2ca3f.1d9488", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": true, - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 740, - "wires": [ - [ - "b52791c3.08967" - ] - ] - }, - { - "id": "80abaee1.5fa7f", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "kind,price,origin\nApple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 740, - "wires": [ - [ - "d4d2ca3f.1d9488" - ] - ] - }, - { - "id": "b52791c3.08967", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 740, - "wires": [] - }, - { - "id": "85091361.85644", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with column name in first row as messages", - "info": "CSV node can use first row of input CSV text as a column name of each record object.\n", - "x": 340, - "y": 680, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json index a97656085..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json @@ -1,99 +0,0 @@ -[ - { - "id": "9e93169c.b763a8", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JavaScript object to CSV", - "info": "CSV node can convert a JavaScript object to CSV text.\nEach object contains key-value pair of specified properties.\n", - "x": 270, - "y": 860, - "wires": [] - }, - { - "id": "8ca41fee.3303d", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 920, - "wires": [ - [ - "c466905b.e8c61" - ] - ] - }, - { - "id": "65146d20.d78204", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 920, - "wires": [ - [ - "92e99e67.a37d8" - ] - ] - }, - { - "id": "c466905b.e8c61", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 430, - "y": 920, - "wires": [ - [ - "65146d20.d78204" - ] - ] - }, - { - "id": "92e99e67.a37d8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 920, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json index 0266dbe49..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json @@ -1,99 +0,0 @@ -[ - { - "id": "e89019c5.70ae78", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert array of JavaScript objects to CSV", - "info": "CSV node can convert an array of JavaScript objects to multi-line CSV text.", - "x": 300, - "y": 1020, - "wires": [] - }, - { - "id": "bd0d82ed.7b28", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 1080, - "wires": [ - [ - "1d857b8d.3a4014" - ] - ] - }, - { - "id": "66a37667.16ebd8", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 1080, - "wires": [ - [ - "859725fd.dc93d8" - ] - ] - }, - { - "id": "1d857b8d.3a4014", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]", - "output": "json", - "x": 430, - "y": 1080, - "wires": [ - [ - "66a37667.16ebd8" - ] - ] - }, - { - "id": "859725fd.dc93d8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 1080, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json index b1b337a06..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json @@ -1,99 +0,0 @@ -[ - { - "id": "2ebdd51e.c5d17a", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert array of JavaScript objects to CSV with column name header", - "info": "CSV node can convert an array of JavaScript objects to multi-line CSV text with column name header at first line.", - "x": 390, - "y": 1200, - "wires": [] - }, - { - "id": "2b4d538d.ada07c", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 1260, - "wires": [ - [ - "3e5c9e8.5065b62" - ] - ] - }, - { - "id": "db02c7be.0984e8", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "all", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 1260, - "wires": [ - [ - "61f8b772.ddb1f8" - ] - ] - }, - { - "id": "3e5c9e8.5065b62", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]", - "output": "json", - "x": 430, - "y": 1260, - "wires": [ - [ - "db02c7be.0984e8" - ] - ] - }, - { - "id": "61f8b772.ddb1f8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 1260, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json index 85901ba36..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json @@ -1,126 +0,0 @@ -[ - { - "id": "b05816ab.7f2b08", - "type": "comment", - "z": "c6ffdacd.d887e8", - "name": "Specify column names in input message", - "info": "Column names can be specified by `columns` property of incoming message.\n", - "x": 240, - "y": 200, - "wires": [] - }, - { - "id": "39205b5c.690684", - "type": "inject", - "z": "c6ffdacd.d887e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 260, - "wires": [ - [ - "526b59ba.2fa068" - ] - ] - }, - { - "id": "b78a407e.2d083", - "type": "csv", - "z": "c6ffdacd.d887e8", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "all", - "multi": "one", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 750, - "y": 260, - "wires": [ - [ - "8b7084dd.986f68" - ] - ] - }, - { - "id": "526b59ba.2fa068", - "type": "template", - "z": "c6ffdacd.d887e8", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]", - "output": "json", - "x": 370, - "y": 260, - "wires": [ - [ - "b204077a.227778" - ] - ] - }, - { - "id": "8b7084dd.986f68", - "type": "debug", - "z": "c6ffdacd.d887e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 930, - "y": 260, - "wires": [] - }, - { - "id": "b204077a.227778", - "type": "change", - "z": "c6ffdacd.d887e8", - "name": "", - "rules": [ - { - "t": "set", - "p": "columns", - "pt": "msg", - "to": "kind,price", - "tot": "str" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 570, - "y": 260, - "wires": [ - [ - "b78a407e.2d083" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json index 81d9b4730..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json @@ -1,200 +0,0 @@ -[ - { - "id": "1ae28939.9f5fc7", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Send column name when reset property set", - "info": "CSV node can send column names at first or `reset` property exists in input message.", - "x": 310, - "y": 1540, - "wires": [] - }, - { - "id": "c16ad95b.4f9ac8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "Apple", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 250, - "y": 1600, - "wires": [ - [ - "7f7bfc72.aed104" - ] - ] - }, - { - "id": "870620b9.95343", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "once", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 650, - "y": 1720, - "wires": [ - [ - "d960de42.619c7" - ] - ] - }, - { - "id": "7f7bfc72.aed104", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 470, - "y": 1600, - "wires": [ - [ - "870620b9.95343" - ] - ] - }, - { - "id": "d960de42.619c7", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 830, - "y": 1720, - "wires": [] - }, - { - "id": "6f8296e.f95ca68", - "type": "inject", - "z": "4b63452d.672afc", - "name": "Orange", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 250, - "y": 1660, - "wires": [ - [ - "c37d0dfa.ec1ab" - ] - ] - }, - { - "id": "c37d0dfa.ec1ab", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n}\n", - "output": "json", - "x": 470, - "y": 1660, - "wires": [ - [ - "870620b9.95343" - ] - ] - }, - { - "id": "35209fe2.16926", - "type": "inject", - "z": "4b63452d.672afc", - "name": "Banana & reset", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - }, - { - "p": "reset", - "v": "", - "vt": "date" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 280, - "y": 1720, - "wires": [ - [ - "afd4e6b3.624a28" - ] - ] - }, - { - "id": "afd4e6b3.624a28", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n}", - "output": "json", - "x": 470, - "y": 1720, - "wires": [ - [ - "870620b9.95343" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json b/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json index 14ef21214..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json @@ -1,94 +0,0 @@ -[ - { - "id": "8c5224a6.201b88", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 180, - "wires": [ - [ - "d6c67e51.0d709" - ] - ] - }, - { - "id": "d6c67e51.0d709", - "type": "template", - "z": "4b63452d.672afc", - "name": "HTML text", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "\n \n List of Fruits\n \n \n
    \n
  • Apple
  • \n
  • Orange
  • \n
  • Banana
  • \n
\n \n\n", - "output": "str", - "x": 390, - "y": 180, - "wires": [ - [ - "599a1155.61a5c" - ] - ] - }, - { - "id": "b0d5cd89.338df", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Extract array of HTML element by CSS selector", - "info": "HTML node can be used to extract elements in HTML document as an array using CSS selector.", - "x": 280, - "y": 120, - "wires": [] - }, - { - "id": "599a1155.61a5c", - "type": "html", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": ".Item", - "ret": "html", - "as": "single", - "x": 550, - "y": 180, - "wires": [ - [ - "942b23d1.cce09" - ] - ] - }, - { - "id": "942b23d1.cce09", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 710, - "y": 180, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json b/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json index ccd3bc782..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json @@ -1,94 +0,0 @@ -[ - { - "id": "a44973e8.6319b", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 360, - "wires": [ - [ - "de1b012e.96ec3" - ] - ] - }, - { - "id": "de1b012e.96ec3", - "type": "template", - "z": "4b63452d.672afc", - "name": "HTML text", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "\n \n List of Fruits\n \n \n
    \n
  • Apple
  • \n
  • Orange
  • \n
  • Banana
  • \n
\n \n\n", - "output": "str", - "x": 390, - "y": 360, - "wires": [ - [ - "cee70712.6f3538" - ] - ] - }, - { - "id": "99e32bc7.c8e508", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Extract sequence of HTML element by CSS selector", - "info": "HTML node can be used to extract elements in HTML document as a messege sequence using CSS selector.", - "x": 290, - "y": 300, - "wires": [] - }, - { - "id": "cee70712.6f3538", - "type": "html", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": ".Item", - "ret": "html", - "as": "multi", - "x": 550, - "y": 360, - "wires": [ - [ - "17f25482.d4b56b" - ] - ] - }, - { - "id": "17f25482.d4b56b", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 710, - "y": 360, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json b/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json index ad2400ef6..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json @@ -1,121 +0,0 @@ -[ - { - "id": "653ce9aa.b6a1c8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 560, - "wires": [ - [ - "52a16f7f.447d8" - ] - ] - }, - { - "id": "52a16f7f.447d8", - "type": "template", - "z": "4b63452d.672afc", - "name": "HTML text", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "\n \n List of Fruits\n \n \n
    \n
  • Apple
  • \n
  • Orange
  • \n
  • Banana
  • \n
\n \n\n", - "output": "str", - "x": 390, - "y": 560, - "wires": [ - [ - "a52319c3.89b008" - ] - ] - }, - { - "id": "8bc35379.31d99", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Extract array of HTML element by CSS selector specified in message", - "info": "CSS selector for HTML node can be specified by `select` property of input message.", - "x": 350, - "y": 500, - "wires": [] - }, - { - "id": "9c49de8a.bad25", - "type": "html", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": "", - "ret": "html", - "as": "single", - "x": 730, - "y": 560, - "wires": [ - [ - "d4f4b987.278a68" - ] - ] - }, - { - "id": "d4f4b987.278a68", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 890, - "y": 560, - "wires": [] - }, - { - "id": "a52319c3.89b008", - "type": "change", - "z": "4b63452d.672afc", - "name": "", - "rules": [ - { - "t": "set", - "p": "select", - "pt": "msg", - "to": ".Item", - "tot": "str" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 560, - "y": 560, - "wires": [ - [ - "9c49de8a.bad25" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json b/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json index c95e71a7d..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json @@ -1,92 +0,0 @@ -[ - { - "id": "9976e95d.2f8398", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 240, - "y": 180, - "wires": [ - [ - "d94fc083.49d87" - ] - ] - }, - { - "id": "6684abb1.8eb454", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JSON string to JS object", - "info": "JSON node can convert JSON string to JavaScript object.", - "x": 250, - "y": 120, - "wires": [] - }, - { - "id": "d94fc083.49d87", - "type": "template", - "z": "4b63452d.672afc", - "name": "JSON string", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "str", - "x": 410, - "y": 180, - "wires": [ - [ - "1a3dc54a.78598b" - ] - ] - }, - { - "id": "8950a55d.023988", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 730, - "y": 180, - "wires": [] - }, - { - "id": "1a3dc54a.78598b", - "type": "json", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "action": "", - "pretty": false, - "x": 570, - "y": 180, - "wires": [ - [ - "8950a55d.023988" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json b/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json index a10cc75b9..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json @@ -1,92 +0,0 @@ -[ - { - "id": "cb13761f.56c328", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 240, - "y": 380, - "wires": [ - [ - "c607642a.78c3c8" - ] - ] - }, - { - "id": "180b1e22.0074e2", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JS object to JSON string", - "info": "JSON node can convert JavaScript object to JSON string.", - "x": 250, - "y": 320, - "wires": [] - }, - { - "id": "c607642a.78c3c8", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 400, - "y": 380, - "wires": [ - [ - "bf309844.fa12e8" - ] - ] - }, - { - "id": "5b6b130b.72a14c", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 730, - "y": 380, - "wires": [] - }, - { - "id": "bf309844.fa12e8", - "type": "json", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "action": "", - "pretty": false, - "x": 570, - "y": 380, - "wires": [ - [ - "5b6b130b.72a14c" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json b/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json index 6271f00c6..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json @@ -1,160 +0,0 @@ -[ - { - "id": "2b18621b.e2670e", - "type": "inject", - "z": "4b63452d.672afc", - "name": "OK", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 230, - "y": 580, - "wires": [ - [ - "5986faee.aef954" - ] - ] - }, - { - "id": "59acf99.9a92308", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Validate input JSON string", - "info": "JSON node can validate input JSON string using [JSON schema](https://json-schema.org/) when converting to JavaScript object.", - "x": 230, - "y": 520, - "wires": [] - }, - { - "id": "5986faee.aef954", - "type": "template", - "z": "4b63452d.672afc", - "name": "JSON string", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "str", - "x": 410, - "y": 580, - "wires": [ - [ - "f8a67c6d.4f1f1" - ] - ] - }, - { - "id": "ca27c92c.ad7cb8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 910, - "y": 580, - "wires": [] - }, - { - "id": "2fad9978.ea1916", - "type": "json", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "action": "", - "pretty": false, - "x": 750, - "y": 580, - "wires": [ - [ - "ca27c92c.ad7cb8" - ] - ] - }, - { - "id": "f8a67c6d.4f1f1", - "type": "template", - "z": "4b63452d.672afc", - "name": "Schema", - "field": "schema", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"type\": \"object\",\n \"properties\": {\n \"kind\": {\n \"type\": \"string\"\n },\n \"price\": {\n \"type\": \"number\"\n },\n \"origin\": {\n \"type\": \"string\"\n }\n }\n}", - "output": "json", - "x": 590, - "y": 580, - "wires": [ - [ - "2fad9978.ea1916" - ] - ] - }, - { - "id": "8337e847.ac18d8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "NG", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 230, - "y": 660, - "wires": [ - [ - "fa14d8bf.1ac938" - ] - ] - }, - { - "id": "fa14d8bf.1ac938", - "type": "template", - "z": "4b63452d.672afc", - "name": "JSON string", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": \"100\",\n \"origin\": \"Canada\"\n}", - "output": "str", - "x": 410, - "y": 660, - "wires": [ - [ - "f8a67c6d.4f1f1" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json b/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json index 3a97c8498..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json @@ -1,92 +0,0 @@ -[ - { - "id": "82f1bd0b.43474", - "type": "xml", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "attr": "", - "chr": "", - "x": 530, - "y": 180, - "wires": [ - [ - "1cd4ad02.9a5423" - ] - ] - }, - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 180, - "wires": [ - [ - "cdd1c154.3a655" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JavaScript object to XML", - "info": "XML node can convert JavaScript object to XML string.", - "x": 240, - "y": 120, - "wires": [] - }, - { - "id": "1cd4ad02.9a5423", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 690, - "y": 180, - "wires": [] - }, - { - "id": "cdd1c154.3a655", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 360, - "y": 180, - "wires": [ - [ - "82f1bd0b.43474" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json b/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json index 5e46428ef..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json @@ -1,92 +0,0 @@ -[ - { - "id": "93e423a9.a407d", - "type": "xml", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "attr": "", - "chr": "", - "x": 530, - "y": 360, - "wires": [ - [ - "2d0dde7e.a50082" - ] - ] - }, - { - "id": "ba1dab90.8d1da8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 360, - "wires": [ - [ - "16617f26.14ced1" - ] - ] - }, - { - "id": "a9f97b00.57d658", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert XML to JavaScript object", - "info": "XML node can convert XML string to JavaScript object.", - "x": 240, - "y": 300, - "wires": [] - }, - { - "id": "2d0dde7e.a50082", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 690, - "y": 360, - "wires": [] - }, - { - "id": "16617f26.14ced1", - "type": "template", - "z": "4b63452d.672afc", - "name": "XML string", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "plain", - "template": "\n\n Apple\n 100\n Canada\n", - "output": "str", - "x": 370, - "y": 360, - "wires": [ - [ - "93e423a9.a407d" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json b/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json index 7a3c39f7e..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json @@ -1,119 +0,0 @@ -[ - { - "id": "581bd648.636628", - "type": "xml", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "attr": "", - "chr": "", - "x": 710, - "y": 540, - "wires": [ - [ - "b74237dc.1e5028" - ] - ] - }, - { - "id": "d0899f9b.f1ac6", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 540, - "wires": [ - [ - "f04ffb9a.68edb8" - ] - ] - }, - { - "id": "8a214c05.dc61f", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Control conversion using options property", - "info": "XML node can control conversion by setting `options` property (defined by [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options)) in input message.", - "x": 260, - "y": 480, - "wires": [] - }, - { - "id": "b74237dc.1e5028", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 870, - "y": 540, - "wires": [] - }, - { - "id": "f04ffb9a.68edb8", - "type": "template", - "z": "4b63452d.672afc", - "name": "XML string", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "plain", - "template": "\n\n Apple\n 100\n Canada\n", - "output": "str", - "x": 370, - "y": 540, - "wires": [ - [ - "fedf79.5889c088" - ] - ] - }, - { - "id": "fedf79.5889c088", - "type": "change", - "z": "4b63452d.672afc", - "name": "set options", - "rules": [ - { - "t": "set", - "p": "options", - "pt": "msg", - "to": "{\"explicitArray\":false}", - "tot": "json" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 550, - "y": 540, - "wires": [ - [ - "581bd648.636628" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json b/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json index d751a8731..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json @@ -1,90 +0,0 @@ -[ - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 180, - "wires": [ - [ - "cdd1c154.3a655" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JavaScript object to YAML", - "info": "YAML node can convert JavaScript object to YAML string.", - "x": 240, - "y": 120, - "wires": [] - }, - { - "id": "1cd4ad02.9a5423", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 670, - "y": 180, - "wires": [] - }, - { - "id": "cdd1c154.3a655", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"fruits\" : {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n }\n}", - "output": "json", - "x": 360, - "y": 180, - "wires": [ - [ - "aaf0100b.16628" - ] - ] - }, - { - "id": "aaf0100b.16628", - "type": "yaml", - "z": "4b63452d.672afc", - "property": "payload", - "name": "", - "x": 510, - "y": 180, - "wires": [ - [ - "1cd4ad02.9a5423" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json b/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json index d80e0d144..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json @@ -1,90 +0,0 @@ -[ - { - "id": "ba1dab90.8d1da8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 360, - "wires": [ - [ - "16617f26.14ced1" - ] - ] - }, - { - "id": "a9f97b00.57d658", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert YAML to JavaScript object", - "info": "YAML node can convert YAML string to JavaScript object.", - "x": 240, - "y": 300, - "wires": [] - }, - { - "id": "2d0dde7e.a50082", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 690, - "y": 360, - "wires": [] - }, - { - "id": "16617f26.14ced1", - "type": "template", - "z": "4b63452d.672afc", - "name": "YAML string", - "field": "payload", - "fieldType": "msg", - "format": "yaml", - "syntax": "plain", - "template": "fruits:\n kind: Apple\n price: 100\n origin: Canada", - "output": "str", - "x": 370, - "y": 360, - "wires": [ - [ - "e2e4f862.f9d7d8" - ] - ] - }, - { - "id": "e2e4f862.f9d7d8", - "type": "yaml", - "z": "4b63452d.672afc", - "property": "payload", - "name": "", - "x": 530, - "y": 360, - "wires": [ - [ - "2d0dde7e.a50082" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json b/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json index 74bb99eb4..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json +++ b/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json @@ -1 +0,0 @@ -[{"id":"6451c8bb.b52278","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":160,"wires":[["cb34307c.ac1dd"]]},{"id":"638546c.38f1fb8","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":160,"wires":[["db5d90ac.bbb3f"]]},{"id":"3ec02cae.012ce4","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as string in ascending order","info":"","x":280,"y":100,"wires":[]},{"id":"db5d90ac.bbb3f","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":160,"wires":[["6451c8bb.b52278"]]},{"id":"cb34307c.ac1dd","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":160,"wires":[]},{"id":"a0de8ca4.6601f","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":480,"wires":[["ca74a53e.90fc08"]]},{"id":"23d253dc.5e990c","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":480,"wires":[["4ccd5aed.ca24e4"]]},{"id":"19946d36.185313","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as string","info":"","x":220,"y":420,"wires":[]},{"id":"4ccd5aed.ca24e4","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":480,"wires":[["a0de8ca4.6601f"]]},{"id":"ca74a53e.90fc08","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":480,"wires":[]},{"id":"d4b49c22.32685","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":true,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":640,"wires":[["45738f07.16416"]]},{"id":"87ce9955.924868","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":640,"wires":[["30b81283.f0772e"]]},{"id":"f6e624df.703a88","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as number","info":"","x":220,"y":580,"wires":[]},{"id":"30b81283.f0772e","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":640,"wires":[["d4b49c22.32685"]]},{"id":"45738f07.16416","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":640,"wires":[]},{"id":"b91c19b1.b66b18","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"price","msgKeyType":"jsonata","seqKey":"payload","seqKeyType":"msg","x":510,"y":800,"wires":[["32dd80a1.226e4"]]},{"id":"adb0daa2.d85a48","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":800,"wires":[["b3ee1b9e.dbf388"]]},{"id":"d1190f8b.9a74b","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array of objects payload using simple JSONata expression","info":"","x":330,"y":740,"wires":[]},{"id":"b3ee1b9e.dbf388","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 80\n },\n {\n \"name\": \"banana\",\n \"price\": 250\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 150\n },\n {\n \"name\": \"kiwi\",\n \"price\": 320\n }\n]","output":"json","x":370,"y":800,"wires":[["b91c19b1.b66b18"]]},{"id":"32dd80a1.226e4","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":800,"wires":[]},{"id":"735ec14e.4ed55","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"descending","as_num":false,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":320,"wires":[["e8dc4ae5.f08598"]]},{"id":"c8ce4e74.9db68","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":320,"wires":[["ea0384a6.1346b8"]]},{"id":"70333397.d78f6c","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as string in descending order","info":"","x":280,"y":260,"wires":[]},{"id":"ea0384a6.1346b8","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":320,"wires":[["735ec14e.4ed55"]]},{"id":"e8dc4ae5.f08598","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":320,"wires":[]},{"id":"e4025b79.fccdd8","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort using object property as sorting key","info":"","x":620,"y":840,"wires":[]},{"id":"2eaa93da.543cac","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of number","info":"","x":600,"y":680,"wires":[]},{"id":"24da49c5.785676","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of string","info":"","x":590,"y":520,"wires":[]},{"id":"dc856174.29eb5","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of string in descending order","info":"","x":650,"y":360,"wires":[]},{"id":"4bab755b.a073dc","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of string in ascending order","info":"","x":650,"y":200,"wires":[]},{"id":"e947beb9.2ec5e","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"$substring(\"0000\" & $string(price), -4) & name","msgKeyType":"jsonata","seqKey":"payload","seqKeyType":"msg","x":510,"y":960,"wires":[["6a4af14a.cafc4"]]},{"id":"d19d7ff5.135f2","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":960,"wires":[["5eb37a3e.3c2184"]]},{"id":"91e34f52.e1413","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array of objects payload using complex JSONata expression","info":"","x":330,"y":900,"wires":[]},{"id":"5eb37a3e.3c2184","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 100\n },\n {\n \"name\": \"banana\",\n \"price\": 200\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 200\n },\n {\n \"name\": \"kiwi\",\n \"price\": 200\n }\n]","output":"json","x":370,"y":960,"wires":[["e947beb9.2ec5e"]]},{"id":"6a4af14a.cafc4","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":960,"wires":[]},{"id":"1b068260.e3200e","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort using object two property (price and name) as sorting keys","info":"","x":690,"y":1000,"wires":[]},{"id":"7ced03b9.75dd8c","type":"comment","z":"9f9f8c22.7e6b2","name":"Example: Sort Array Payload","info":"Sort node can be used to message payload that points to a JavaScript array. It can specify sort order and sort key. Sort key can be payload value or JSONata expression. If JSONata expression is used, the expression is applied to `payload` value.\n","x":180,"y":40,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json b/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json index abcd3cfdb..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json @@ -1 +0,0 @@ -[{"id":"60b477b9.188ce8","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":180,"wires":[["38fcd115.f147ae"]]},{"id":"35b577bf.827978","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":180,"wires":[["5a8dfb10.5ed8b4"]]},{"id":"e8e4517b.e2139","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as string in ascending order","info":"Sort node can be used to message sequence that contains `parts` property. If JSONata expression is used, the expression is applied to input message.","x":280,"y":100,"wires":[]},{"id":"5a8dfb10.5ed8b4","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":180,"wires":[["a509486a.9c32a8"]]},{"id":"38fcd115.f147ae","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":180,"wires":[]},{"id":"eca99b1d.c456c8","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort message sequence in ascending order","info":"","x":770,"y":220,"wires":[]},{"id":"a509486a.9c32a8","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":180,"wires":[["60b477b9.188ce8"]]},{"id":"56294459.e2431c","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":140,"wires":[]},{"id":"f8ab27f2.b81d78","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":520,"wires":[["75369287.5bb00c"]]},{"id":"df79c07e.d9dce","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":520,"wires":[["8e1e5b1e.a979e8"]]},{"id":"de7c57f3.7ecae8","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as string","info":"","x":220,"y":440,"wires":[]},{"id":"8e1e5b1e.a979e8","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":520,"wires":[["4392ddd6.e30404"]]},{"id":"75369287.5bb00c","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":520,"wires":[]},{"id":"f9100b9d.065088","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":true,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":700,"wires":[["53127c8c.cb4b64"]]},{"id":"c010d098.ac0e8","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":700,"wires":[["902c97aa.8b2958"]]},{"id":"8531cf78.dcdfc","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as number","info":"","x":220,"y":620,"wires":[]},{"id":"902c97aa.8b2958","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":700,"wires":[["99461434.772718"]]},{"id":"53127c8c.cb4b64","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":700,"wires":[]},{"id":"ea246a52.4b9d88","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"price","msgKeyType":"jsonata","seqKey":"payload.price","seqKeyType":"jsonata","x":650,"y":880,"wires":[["2d548e4c.9adbd2"]]},{"id":"d69bd6df.849d68","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":880,"wires":[["e6ef0fbc.6fc22"]]},{"id":"ee5f9da5.d21ee","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array of objects payload using simple JSONata expression","info":"","x":330,"y":800,"wires":[]},{"id":"e6ef0fbc.6fc22","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 80\n },\n {\n \"name\": \"banana\",\n \"price\": 250\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 150\n },\n {\n \"name\": \"kiwi\",\n \"price\": 320\n }\n]","output":"json","x":370,"y":880,"wires":[["fcfd304c.562c9"]]},{"id":"2d548e4c.9adbd2","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":880,"wires":[]},{"id":"4ec23d0b.a4f014","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"descending","as_num":false,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":340,"wires":[["b1f8d641.a96a78"]]},{"id":"4aa6a253.159aec","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":340,"wires":[["f28101b7.f4b21"]]},{"id":"63d42ecf.fd933","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as string in descending order","info":"","x":280,"y":260,"wires":[]},{"id":"f28101b7.f4b21","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":340,"wires":[["685d7c1b.4f92b4"]]},{"id":"b1f8d641.a96a78","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":340,"wires":[]},{"id":"c112dd05.1543e","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort using object property as sorting key","info":"","x":760,"y":920,"wires":[]},{"id":"5c5ff25a.0f22bc","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort payload as array of number","info":"","x":740,"y":740,"wires":[]},{"id":"d0270e9b.04b7d","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort payload as array of string","info":"","x":730,"y":560,"wires":[]},{"id":"6ae587cd.cf9478","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort payload as array of string in descending order","info":"","x":790,"y":380,"wires":[]},{"id":"6fba3dab.8218c4","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"$substring(\"0000\" & $string(price), -4) & name","msgKeyType":"jsonata","seqKey":"$substring(\"0000\" & $string(payload.price), -4) & payload.name","seqKeyType":"jsonata","x":650,"y":1060,"wires":[["95f7f1b0.39fac"]]},{"id":"892b1f19.60247","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":1060,"wires":[["80ca6396.f55bb"]]},{"id":"c2da4a48.d35cd8","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array of objects payload using complex JSONata expression","info":"","x":330,"y":980,"wires":[]},{"id":"80ca6396.f55bb","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 100\n },\n {\n \"name\": \"banana\",\n \"price\": 200\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 200\n },\n {\n \"name\": \"kiwi\",\n \"price\": 200\n }\n]","output":"json","x":370,"y":1060,"wires":[["4cda7453.75eb1c"]]},{"id":"95f7f1b0.39fac","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":1060,"wires":[]},{"id":"24472c63.d12294","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort using object two property (price and name) as sorting keys","info":"","x":830,"y":1100,"wires":[]},{"id":"685d7c1b.4f92b4","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":340,"wires":[["4ec23d0b.a4f014"]]},{"id":"4392ddd6.e30404","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":520,"wires":[["f8ab27f2.b81d78"]]},{"id":"99461434.772718","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":700,"wires":[["f9100b9d.065088"]]},{"id":"fcfd304c.562c9","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":880,"wires":[["ea246a52.4b9d88"]]},{"id":"4cda7453.75eb1c","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":1060,"wires":[["6fba3dab.8218c4"]]},{"id":"1b2b8efc.b5aa51","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":300,"wires":[]},{"id":"972a454c.99b5e8","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":480,"wires":[]},{"id":"39123b6a.ee5bb4","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":660,"wires":[]},{"id":"7809a5ac.eb0f8c","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":840,"wires":[]},{"id":"f62c6678.d4da08","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":1020,"wires":[]},{"id":"2438c53f.71a0da","type":"comment","z":"62d2b20a.1a87bc","name":"Example: Sort Message Sequence","info":"Sort node can be used to message sequence that contains `parts` property. If JSONata expression is used, the expression is applied to input message.","x":200,"y":40,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json b/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json index 3fb14f0c3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json +++ b/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json @@ -1 +0,0 @@ -[{"id":"f94ffc33.76f83","type":"comment","z":"e5679299.d9792","name":"Example: Split Message Payload","info":"Split node can be used to split message payload into multiple messages.","x":190,"y":60,"wires":[]},{"id":"657bb57c.a3f98c","type":"split","z":"e5679299.d9792","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":510,"y":160,"wires":[["14228ff.ae24f7"]]},{"id":"2afece55.b87de2","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":160,"wires":[["cfca3863.d961d8"]]},{"id":"cfca3863.d961d8","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"Apple\nOrange\nBanana","output":"str","x":370,"y":160,"wires":[["657bb57c.a3f98c"]]},{"id":"14228ff.ae24f7","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":660,"y":160,"wires":[]},{"id":"9a3c9494.b5d178","type":"comment","z":"e5679299.d9792","name":"Split input text by specified string","info":"","x":230,"y":120,"wires":[]},{"id":"38c873c.5ae718c","type":"comment","z":"e5679299.d9792","name":"↑ split by newline (\\\\n)","info":"","x":560,"y":200,"wires":[]},{"id":"bdfa12b9.3fbbc","type":"split","z":"e5679299.d9792","name":"","splt":"4","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":510,"y":280,"wires":[["debf23bb.c0245"]]},{"id":"7c0948db.e35d38","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":280,"wires":[["7f791b7d.94cad4"]]},{"id":"7f791b7d.94cad4","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"Apple\nOrange\nBanana","output":"str","x":370,"y":280,"wires":[["bdfa12b9.3fbbc"]]},{"id":"debf23bb.c0245","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":660,"y":280,"wires":[]},{"id":"f05e98a3.4182c8","type":"comment","z":"e5679299.d9792","name":"Split input text by specified number of characters","info":"","x":280,"y":240,"wires":[]},{"id":"86b52b51.2258d8","type":"comment","z":"e5679299.d9792","name":"↑ split by four characters","info":"","x":570,"y":320,"wires":[]},{"id":"71d7c0e0.c0316","type":"split","z":"e5679299.d9792","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":510,"y":420,"wires":[["bee5b6a2.a955a8"]]},{"id":"1cdc2df9.bebdd2","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":420,"wires":[["f30df13c.19475"]]},{"id":"f30df13c.19475","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"[ \n \"Apple\",\n \"Orange\",\n \"Banana\"\n]","output":"json","x":370,"y":420,"wires":[["71d7c0e0.c0316"]]},{"id":"bee5b6a2.a955a8","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":660,"y":420,"wires":[]},{"id":"b2c731fe.abda4","type":"comment","z":"e5679299.d9792","name":"Split input array","info":"","x":180,"y":380,"wires":[]},{"id":"1f557227.d0910e","type":"comment","z":"e5679299.d9792","name":"↑ split array","info":"","x":530,"y":460,"wires":[]},{"id":"c0d43ff4.291d8","type":"split","z":"e5679299.d9792","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"topic","x":510,"y":540,"wires":[["fc9fe458.50fd18"]]},{"id":"6d52ce8a.0c715","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":540,"wires":[["bdeb8c21.1c6b7"]]},{"id":"bdeb8c21.1c6b7","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"{ \n \"Apple\": 80,\n \"Orange\": 100,\n \"Banana\": 50\n}","output":"json","x":370,"y":540,"wires":[["c0d43ff4.291d8"]]},{"id":"fc9fe458.50fd18","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":640,"y":540,"wires":[]},{"id":"48956e14.79b86","type":"comment","z":"e5679299.d9792","name":"Split object to key/value pairs","info":"","x":220,"y":500,"wires":[]},{"id":"d528c2c2.6efc7","type":"comment","z":"e5679299.d9792","name":"↑ split object","info":"","x":530,"y":580,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file-in/01 - Read string from a file.json b/packages/node_modules/@node-red/nodes/examples/storage/file-in/01 - Read string from a file.json index 14d7152c7..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file-in/01 - Read string from a file.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file-in/01 - Read string from a file.json @@ -1,113 +0,0 @@ -[ - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "194a3e4f.a92772", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 220, - "wires": [ - [ - "b4b9f603.739598" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "Write string to a file, then read from the file", - "info": "File-in node can read string from a file.", - "x": 260, - "y": 140, - "wires": [] - }, - { - "id": "b4b9f603.739598", - "type": "file", - "z": "194a3e4f.a92772", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 420, - "y": 220, - "wires": [ - [ - "6dc01cac.5c4bf4" - ] - ] - }, - { - "id": "2587adb9.7e60f2", - "type": "debug", - "z": "194a3e4f.a92772", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 810, - "y": 220, - "wires": [] - }, - { - "id": "6dc01cac.5c4bf4", - "type": "file in", - "z": "194a3e4f.a92772", - "name": "", - "filename": "/tmp/hello.txt", - "format": "utf8", - "chunk": false, - "sendError": false, - "encoding": "none", - "x": 620, - "y": 220, - "wires": [ - [ - "2587adb9.7e60f2" - ] - ] - }, - { - "id": "f4b4309a.3b78a", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "↑read result from file", - "info": "", - "x": 630, - "y": 260, - "wires": [] - }, - { - "id": "672d3693.3cabd8", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "↓write to /tmp/hello.txt", - "info": "", - "x": 440, - "y": 180, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file-in/02 - Read data in specified encoding.json b/packages/node_modules/@node-red/nodes/examples/storage/file-in/02 - Read data in specified encoding.json index d96623fb1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file-in/02 - Read data in specified encoding.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file-in/02 - Read data in specified encoding.json @@ -1,113 +0,0 @@ -[ - { - "id": "8997398f.c5d628", - "type": "inject", - "z": "194a3e4f.a92772", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "😀", - "payloadType": "str", - "x": 210, - "y": 480, - "wires": [ - [ - "56e32d23.050f44" - ] - ] - }, - { - "id": "4e598e65.1799d", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "Read data in specified encoding", - "info": "File-in node can specify encoding of data read from a file.", - "x": 230, - "y": 400, - "wires": [] - }, - { - "id": "56e32d23.050f44", - "type": "file", - "z": "194a3e4f.a92772", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 380, - "y": 480, - "wires": [ - [ - "38fa0579.f2cd8a" - ] - ] - }, - { - "id": "d28c8994.99c0a8", - "type": "debug", - "z": "194a3e4f.a92772", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 770, - "y": 480, - "wires": [] - }, - { - "id": "38fa0579.f2cd8a", - "type": "file in", - "z": "194a3e4f.a92772", - "name": "", - "filename": "/tmp/hello.txt", - "format": "utf8", - "chunk": false, - "sendError": false, - "encoding": "base64", - "x": 580, - "y": 480, - "wires": [ - [ - "d28c8994.99c0a8" - ] - ] - }, - { - "id": "fa22ca20.ae4528", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "↑read data from file as base64 string", - "info": "", - "x": 640, - "y": 520, - "wires": [] - }, - { - "id": "148e25ad.98891a", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "↓write to /tmp/hello.txt", - "info": "", - "x": 400, - "y": 440, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file-in/03 - Read data breaking lines into messages.json b/packages/node_modules/@node-red/nodes/examples/storage/file-in/03 - Read data breaking lines into messages.json index b3d35a4da..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file-in/03 - Read data breaking lines into messages.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file-in/03 - Read data breaking lines into messages.json @@ -1,132 +0,0 @@ -[ - { - "id": "6a0b1d03.d4cee4", - "type": "inject", - "z": "194a3e4f.a92772", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 740, - "wires": [ - [ - "d4b00cb7.a5a23" - ] - ] - }, - { - "id": "f17ea1d1.8ecc3", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "Read data breaking lines into individual messages", - "info": "File-in node can break read text into messages with individual lines", - "x": 290, - "y": 660, - "wires": [] - }, - { - "id": "99ae7806.1d6428", - "type": "file", - "z": "194a3e4f.a92772", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 540, - "y": 740, - "wires": [ - [ - "70d7892f.d27db8" - ] - ] - }, - { - "id": "7ed8282c.92b338", - "type": "debug", - "z": "194a3e4f.a92772", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 810, - "y": 800, - "wires": [] - }, - { - "id": "70d7892f.d27db8", - "type": "file in", - "z": "194a3e4f.a92772", - "name": "", - "filename": "/tmp/hello.txt", - "format": "lines", - "chunk": false, - "sendError": false, - "encoding": "none", - "x": 620, - "y": 800, - "wires": [ - [ - "7ed8282c.92b338" - ] - ] - }, - { - "id": "c1b7e05.1d94b2", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "↑read data from file breaking lines into messages", - "info": "", - "x": 720, - "y": 840, - "wires": [] - }, - { - "id": "a5f647b2.cf27a8", - "type": "comment", - "z": "194a3e4f.a92772", - "name": "↓write to /tmp/hello.txt", - "info": "", - "x": 560, - "y": 700, - "wires": [] - }, - { - "id": "d4b00cb7.a5a23", - "type": "template", - "z": "194a3e4f.a92772", - "name": "data", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "one\ntwo\nthree!", - "output": "str", - "x": 370, - "y": 740, - "wires": [ - [ - "99ae7806.1d6428" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file/01 - Write string to a file.json b/packages/node_modules/@node-red/nodes/examples/storage/file/01 - Write string to a file.json index 2be033021..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file/01 - Write string to a file.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file/01 - Write string to a file.json @@ -1,113 +0,0 @@ -[ - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 200, - "wires": [ - [ - "b4b9f603.739598" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Write string to a file, then read from the file", - "info": "File node can write string to a file.", - "x": 260, - "y": 120, - "wires": [] - }, - { - "id": "b4b9f603.739598", - "type": "file", - "z": "4b63452d.672afc", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 420, - "y": 200, - "wires": [ - [ - "6dc01cac.5c4bf4" - ] - ] - }, - { - "id": "2587adb9.7e60f2", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 810, - "y": 200, - "wires": [] - }, - { - "id": "6dc01cac.5c4bf4", - "type": "file in", - "z": "4b63452d.672afc", - "name": "", - "filename": "/tmp/hello.txt", - "format": "utf8", - "chunk": false, - "sendError": false, - "encoding": "none", - "x": 620, - "y": 200, - "wires": [ - [ - "2587adb9.7e60f2" - ] - ] - }, - { - "id": "f4b4309a.3b78a", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↑read result from file", - "info": "", - "x": 630, - "y": 240, - "wires": [] - }, - { - "id": "672d3693.3cabd8", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↓write to /tmp/hello.txt", - "info": "", - "x": 440, - "y": 160, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file/02 - Write string to a file specified by property.json b/packages/node_modules/@node-red/nodes/examples/storage/file/02 - Write string to a file specified by property.json index 6aaff500a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file/02 - Write string to a file specified by property.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file/02 - Write string to a file specified by property.json @@ -1,118 +0,0 @@ -[ - { - "id": "704479e1.399388", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "filename", - "v": "/tmp/hello.txt", - "vt": "str" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 400, - "wires": [ - [ - "402f3b7e.988014" - ] - ] - }, - { - "id": "8e876a75.e9beb8", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Write string to a file specied by filename property, the read from the file", - "info": "File node can target file using `filename` property.", - "x": 350, - "y": 320, - "wires": [] - }, - { - "id": "402f3b7e.988014", - "type": "file", - "z": "4b63452d.672afc", - "name": "", - "filename": "", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 390, - "y": 400, - "wires": [ - [ - "26e077d6.bbcd98" - ] - ] - }, - { - "id": "97b6b6b2.a54b38", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 770, - "y": 400, - "wires": [] - }, - { - "id": "26e077d6.bbcd98", - "type": "file in", - "z": "4b63452d.672afc", - "name": "", - "filename": "/tmp/hello.txt", - "format": "utf8", - "chunk": false, - "sendError": false, - "encoding": "none", - "x": 580, - "y": 400, - "wires": [ - [ - "97b6b6b2.a54b38" - ] - ] - }, - { - "id": "85062297.da79", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↑read result from file", - "info": "", - "x": 590, - "y": 440, - "wires": [] - }, - { - "id": "7316c4fc.b1dcdc", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↓write to file specified by filename property", - "info": "", - "x": 500, - "y": 360, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file/03 - Delete a file.json b/packages/node_modules/@node-red/nodes/examples/storage/file/03 - Delete a file.json index fe6ac166b..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file/03 - Delete a file.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file/03 - Delete a file.json @@ -1,85 +0,0 @@ -[ - { - "id": "4ac00fb0.d5f52", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 600, - "wires": [ - [ - "542cc2f4.92857c" - ] - ] - }, - { - "id": "671f8295.0e6f6c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Delete a file", - "info": "File node can delete a file.", - "x": 170, - "y": 540, - "wires": [] - }, - { - "id": "542cc2f4.92857c", - "type": "file", - "z": "4b63452d.672afc", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "delete", - "encoding": "none", - "x": 420, - "y": 600, - "wires": [ - [ - "a24da523.5babe8" - ] - ] - }, - { - "id": "a24da523.5babe8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 630, - "y": 600, - "wires": [] - }, - { - "id": "51157051.2f62", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↓delete a file", - "info": "", - "x": 390, - "y": 560, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/file/04 - Specify encoding of written data.json b/packages/node_modules/@node-red/nodes/examples/storage/file/04 - Specify encoding of written data.json index 4dabbd670..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/file/04 - Specify encoding of written data.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/file/04 - Specify encoding of written data.json @@ -1,113 +0,0 @@ -[ - { - "id": "e4ef1f5e.7cd82", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "8J+YgA==", - "payloadType": "str", - "x": 220, - "y": 820, - "wires": [ - [ - "72b37cc8.177054" - ] - ] - }, - { - "id": "f5997af4.5a9298", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Specify encoding of written data", - "info": "File node can specify encoding of data.", - "x": 230, - "y": 740, - "wires": [] - }, - { - "id": "72b37cc8.177054", - "type": "file", - "z": "4b63452d.672afc", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "base64", - "x": 400, - "y": 820, - "wires": [ - [ - "2da33ec.f45cac2" - ] - ] - }, - { - "id": "2e814354.278c8c", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 790, - "y": 820, - "wires": [] - }, - { - "id": "2da33ec.f45cac2", - "type": "file in", - "z": "4b63452d.672afc", - "name": "", - "filename": "/tmp/hello.txt", - "format": "utf8", - "chunk": false, - "sendError": false, - "encoding": "none", - "x": 600, - "y": 820, - "wires": [ - [ - "2e814354.278c8c" - ] - ] - }, - { - "id": "ec754c99.84bfd", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↓write string with base64 encoding", - "info": "", - "x": 460, - "y": 780, - "wires": [] - }, - { - "id": "3e6704ff.4ce25c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "↑read result from file", - "info": "", - "x": 610, - "y": 860, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json b/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json index 873e99b89..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json @@ -1,108 +0,0 @@ -[ - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "a7ac8a68.0f7218", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 160, - "wires": [ - [ - "b4b9f603.739598" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "a7ac8a68.0f7218", - "name": "Watch changes of a file", - "info": "Watch node can watch and report changes of a file.", - "x": 200, - "y": 80, - "wires": [] - }, - { - "id": "b4b9f603.739598", - "type": "file", - "z": "a7ac8a68.0f7218", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 420, - "y": 160, - "wires": [ - [] - ] - }, - { - "id": "672d3693.3cabd8", - "type": "comment", - "z": "a7ac8a68.0f7218", - "name": "↓write to /tmp/hello.txt", - "info": "", - "x": 440, - "y": 120, - "wires": [] - }, - { - "id": "15f1f5aa.506ffa", - "type": "watch", - "z": "a7ac8a68.0f7218", - "name": "", - "files": "/tmp/hello.txt", - "recursive": "", - "x": 410, - "y": 200, - "wires": [ - [ - "a91562b9.ca805" - ] - ] - }, - { - "id": "a91562b9.ca805", - "type": "debug", - "z": "a7ac8a68.0f7218", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 610, - "y": 200, - "wires": [] - }, - { - "id": "2ab4eba8.267d64", - "type": "comment", - "z": "a7ac8a68.0f7218", - "name": "↑watch changes of /tmp/hello.txt", - "info": "", - "x": 470, - "y": 240, - "wires": [] - } -] \ No newline at end of file diff --git a/test/editor/editor_helper.js b/test/editor/editor_helper.js deleted file mode 100644 index 73177c2ca..000000000 --- a/test/editor/editor_helper.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require("when"); -var http = require('http'); -var express = require("express"); -var fs = require('fs-extra'); -var path = require('path'); -var app = express(); - -var RED = require("nr-test-utils").require("node-red/lib/red.js"); - -var utilPage = require("./pageobjects/util/util_page"); - -var server; -var homeDir = './test/resources/home'; -var address = '127.0.0.1'; -var listenPort = 0; // use ephemeral port -var url; -/* - * Set false when you need a flow to reproduce the failed test case. - * The flow file is under "homeDir" defined above. - */ -var isDeleteFlow = true; - -function getFlowFilename() { - var orig = Error.prepareStackTrace; - var err = new Error(); - Error.prepareStackTrace = function (err, stack) { - return stack; - }; - // Two level higher caller is the actual caller (e.g. a caller of startServer). - var filepath = err.stack[2].getFileName(); - var filename = path.basename(filepath, ".js"); - Error.prepareStackTrace = orig; - - var flowFile = 'flows_' + filename + '.json'; - return flowFile; -} - -function cleanup(flowFile) { - var credentialFile = flowFile.replace(/\.json$/, '') + '_cred.json'; - deleteFile(homeDir + "/" + flowFile); - deleteFile(homeDir + "/." + flowFile + ".backup"); - deleteFile(homeDir + "/" + credentialFile); - deleteFile(homeDir + "/." + credentialFile + ".backup"); - deleteFile(homeDir + "/package.json"); - deleteFile(homeDir + "/lib/flows"); - deleteFile(homeDir + "/lib"); -} - -function deleteFile(flowFile) { - try { - fs.statSync(flowFile); - if (isDeleteFlow) { - fs.unlinkSync(flowFile); - } - } catch (e) {} -} - -module.exports = { - startServer: function() { - try{ - utilPage.init(); - - // Name a flow file including caller filename so that multiple Node-RED servers can run simultaneously. - // Call this method here because retrieving the caller filename by call stack. - var flowFilename = getFlowFilename(); - browser.windowHandleMaximize(); - browser.call(function () { - return new Promise(function(resolve, reject) { - cleanup(flowFilename); - server = http.createServer(app); - var settings = { - httpAdminRoot: "/", - httpNodeRoot: "/api", - userDir: homeDir, - flowFile: flowFilename, - functionGlobalContext: { }, // enables global context - SKIP_BUILD_CHECK: true, - logging: {console: {level:'off'}} - }; - RED.init(server, settings); - app.use(settings.httpAdminRoot,RED.httpAdmin); - app.use(settings.httpNodeRoot,RED.httpNode); - server.listen(listenPort, address); - server.on('listening', function() { - var port = server.address().port; - url = 'http://' + address + ':' + port; - }); - RED.start().then(function() { - resolve(); - }); - }); - }); - browser.url(url); - browser.waitForExist(".red-ui-palette-node[data-palette-type='inject']"); - } catch (err) { - console.log(err); - throw err; - } - }, - - stopServer: function(done) { - try { - // Call this method here because retrieving the caller filename by call stack. - var flowFilename = getFlowFilename(); - browser.call(function () { - browser.close(); // need to call this inside browser.call(). - return when.promise(function(resolve, reject) { - if (server) { - RED.stop().then(function() { - server.close(function() { - cleanup(flowFilename); - resolve(); - }); - }); - } else { - cleanup(flowFilename); - resolve(); - } - }); - }); - } catch (err) { - console.log(err); - throw err; - } - }, - - url: function() { - return url; - }, - - red: function() { - return RED; - }, -}; diff --git a/test/editor/pageobjects/editor/debugTab_page.js b/test/editor/pageobjects/editor/debugTab_page.js deleted file mode 100644 index 5476a28d6..000000000 --- a/test/editor/pageobjects/editor/debugTab_page.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -function open(retainMessage) { - browser.clickWithWait('#red-ui-tab-debug-link-button'); - - if (!retainMessage) { - // Clear old messages - browser.clickWithWait('//a[@id="red-ui-sidebar-debug-clear"]'); - } -} - -function getMessage(index) { - index = index ? index : 1; - var debugMessagePath = '//div[@class="red-ui-debug-content red-ui-debug-content-list"]/div[contains(@class,"red-ui-debug-msg")][' + index + ']//span[contains(@class, "red-ui-debug-msg-type")]'; - return browser.getTextWithWait(debugMessagePath); -} - -function clearMessage() { - browser.clickWithWait('//a[@id="red-ui-sidebar-debug-clear"]'); -} - -module.exports = { - open: open, - getMessage: getMessage, - clearMessage: clearMessage, -}; diff --git a/test/editor/pageobjects/editor/palette_page.js b/test/editor/pageobjects/editor/palette_page.js deleted file mode 100644 index 3b484a58c..000000000 --- a/test/editor/pageobjects/editor/palette_page.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var idMap = { - // common - "inject": ".red-ui-palette-node[data-palette-type='inject']", - "debug": ".red-ui-palette-node[data-palette-type='debug']", - "complete": ".red-ui-palette-node[data-palette-type='complete']", - "catch": ".red-ui-palette-node[data-palette-type='catch']", - "status": ".red-ui-palette-node[data-palette-type='status']", - "comment": ".red-ui-palette-node[data-palette-type='comment']", - // function - "function": ".red-ui-palette-node[data-palette-type='function']", - "switch": ".red-ui-palette-node[data-palette-type='switch']", - "change": ".red-ui-palette-node[data-palette-type='change']", - "range": ".red-ui-palette-node[data-palette-type='range']", - "template": ".red-ui-palette-node[data-palette-type='template']", - "delay": ".red-ui-palette-node[data-palette-type='delay']", - "trigger": ".red-ui-palette-node[data-palette-type='trigger']", - "exec": ".red-ui-palette-node[data-palette-type='exec']", - // network - "mqttIn": ".red-ui-palette-node[data-palette-type='mqtt in']", - "mqttOut": ".red-ui-palette-node[data-palette-type='mqtt out']", - "httpIn": ".red-ui-palette-node[data-palette-type='http in']", - "httpResponse": ".red-ui-palette-node[data-palette-type='http response']", - "httpRequest": ".red-ui-palette-node[data-palette-type='http request']", - "websocketIn": ".red-ui-palette-node[data-palette-type='websocket in']", - "websocketOut": ".red-ui-palette-node[data-palette-type='websocket out']", - // sequence - "split": ".red-ui-palette-node[data-palette-type='split']", - "join": ".red-ui-palette-node[data-palette-type='join']", - "batch": ".red-ui-palette-node[data-palette-type='batch']", - // parser - "csv": ".red-ui-palette-node[data-palette-type='csv']", - "html": ".red-ui-palette-node[data-palette-type='html']", - "json": ".red-ui-palette-node[data-palette-type='json']", - "xml": ".red-ui-palette-node[data-palette-type='xml']", - "yaml": ".red-ui-palette-node[data-palette-type='yaml']", - // storage - "fileIn": ".red-ui-palette-node[data-palette-type='file in']", -}; - -function getId(type) { - return idMap[type]; -} - -module.exports = { - getId: getId, -}; diff --git a/test/editor/pageobjects/editor/workspace_page.js b/test/editor/pageobjects/editor/workspace_page.js deleted file mode 100644 index 92a725dac..000000000 --- a/test/editor/pageobjects/editor/workspace_page.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require("when"); -var events = require("nr-test-utils").require("@node-red/runtime/lib/events.js"); -var palette = require("./palette_page"); -var nodeFactory = require("../nodes/nodefactory_page"); -var keyPage = require("../util/key_page"); -var flowLayout = { - flowRightEnd : 600, - widthInterval : 300, - heightInterval : 80 -}; -var previousX = -flowLayout.widthInterval; -var previousY = 0; - -function addNode(type, x, y) { - if (x !== undefined) { - previousX = x; - if (y !== undefined) { - previousY = y; - } - } else { - if (previousX < flowLayout.flowRightEnd) { - previousX = previousX + flowLayout.widthInterval; - } else { - previousX = 0; - previousY = previousY + flowLayout.heightInterval; - } - } - browser.waitForVisible('#red-ui-palette-search'); - browser.setValue('//*[@id="red-ui-palette-search"]/div/form/input', type.replace(/([A-Z])/g, ' $1').toLowerCase()); - browser.pause(300); - browser.waitForVisible(palette.getId(type)); - browser.moveToObject(palette.getId(type)); - browser.buttonDown(); - browser.moveToObject("#red-ui-palette-search", previousX + 300, previousY + 100); // adjust to the top-left corner of workspace. - browser.buttonUp(); - // Last node is the one that has been created right now. - var nodeElement = browser.elements('//*[contains(concat(" ", normalize-space(@class), " "), " red-ui-flow-node-group ")][last()]'); - var nodeId = nodeElement.getAttribute('id'); - var node = nodeFactory.create(type, nodeId); - return node; -} - -function deleteAllNodes() { - browser.waitForVisible('//*[contains(@class, "active")]/a[@class="red-ui-tab-label"]'); - browser.click('//*[contains(@class, "active")]/a[@class="red-ui-tab-label"]'); - browser.pause(1000); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); -} - -function deploy() { - browser.call(function () { - return when.promise(function (resolve, reject) { - events.on("runtime-event", function (event) { - if (event.id === 'runtime-deploy') { - events.removeListener("runtime-event", arguments.callee); - resolve(); - } - }); - browser.clickWithWait('#red-ui-header-button-deploy'); - }); - }); - browser.waitForText('#red-ui-header-button-deploy', 10000); - // Need additional wait until buttons becomes clickable. - browser.pause(50); -} - -function init(width, height) { - deleteAllNodes(); - - if (width !== undefined) { - flowLayout.widthInterval = width; - } - if (height !== undefined) { - flowLayout.heightInterval = height; - } - previousX = -flowLayout.widthInterval; - previousY = 0; -} - -module.exports = { - addNode: addNode, - deploy: deploy, - init: init -}; diff --git a/test/editor/pageobjects/nodes/core/common/20-inject_page.js b/test/editor/pageobjects/nodes/core/common/20-inject_page.js deleted file mode 100644 index b7c230e6c..000000000 --- a/test/editor/pageobjects/nodes/core/common/20-inject_page.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function injectNode(id) { - nodePage.call(this, id); -} - -util.inherits(injectNode, nodePage); - -var payloadTypeList = { - "msg": 1, - "flow": 2, - "global": 3, - "str": 4, - "num": 5, - "bool": 6, - "json": 7, - "bin": 8, - "date": 9, - "jsonata": 10, - "env": 11, -}; - -var repeatTypeList = { - "none": 1, - "interval": 2, - "intervalBetweenTimes": 3, - "atASpecificTime": 4, -}; - -injectNode.prototype.setPayload = function(payloadType, payload) { - // Open a payload type list. - browser.clickWithWait('//*[@id="node-input-property-container"]/li[1]/div/div/div[3]'); - // Select a payload type. - var payloadTypeXPath = '//*[contains(@class, "red-ui-typedInput-options")]/a[' + payloadTypeList[payloadType] + ']'; - browser.clickWithWait(payloadTypeXPath); - if (payload) { - // Input a value. - browser.setValue('//*[@id="node-input-property-container"]/li[1]/div/div/div[3]/div[1]/input', payload); - } -} - -injectNode.prototype.setTopic = function(topic) { - browser.setValue('//*[@id="node-input-property-container"]/li[2]/div/div/div[3]/div[1]/input', topic); -} - -injectNode.prototype.setOnce = function(once) { - var isChecked = browser.isSelected('#node-input-once'); - if (isChecked !== once) { - browser.clickWithWait('#node-input-once'); - } -} - -injectNode.prototype.setRepeat = function(repeatType) { - var repeatTypeXPath = '//*[@id="inject-time-type-select"]/option[' + repeatTypeList[repeatType] + ']'; - browser.clickWithWait(repeatTypeXPath); -} - -injectNode.prototype.setRepeatInterval = function(repeat) { - browser.setValue('#inject-time-interval-count', repeat); -} - -module.exports = injectNode; diff --git a/test/editor/pageobjects/nodes/core/common/21-debug_page.js b/test/editor/pageobjects/nodes/core/common/21-debug_page.js deleted file mode 100644 index 3cec19985..000000000 --- a/test/editor/pageobjects/nodes/core/common/21-debug_page.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function debugNode(id) { - nodePage.call(this, id); -} - -util.inherits(debugNode, nodePage); - -debugNode.prototype.setOutput = function (complete) { - // Open a payload type list. - browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-container")]/button'); - if (complete !== 'true') { - // Select the "msg" type. - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options")][2]/a[1]'); - // Input the path in msg. - browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-input")]/input'); - browser.keys(Array('payload'.length).fill('Backspace')); - browser.setValue('//*[@id="dialog-form"]/div[1]/div/div[1]/input', complete); - } else { - // Select the "complete msg object" type. - browser.clickWithWait('/html/body/div[11]/a[2]'); - } -} - -module.exports = debugNode; diff --git a/test/editor/pageobjects/nodes/core/common/24-complete_page.js b/test/editor/pageobjects/nodes/core/common/24-complete_page.js deleted file mode 100644 index 558ee709a..000000000 --- a/test/editor/pageobjects/nodes/core/common/24-complete_page.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function completeNode(id) { - nodePage.call(this, id); -} - -util.inherits(completeNode, nodePage); - -completeNode.prototype.setScope = function (scope) { - if (scope) { - browser.clickWithWait('//*[@id="node-input-complete-target-select"]'); - browser.pause(1000); - if (Array.isArray(scope)) { - for (var i = 0; i < scope.length; i++) { - browser.moveToObject(scope[i].id); - browser.buttonDown(); - browser.buttonUp(); - } - } else { - browser.moveToObject(scope.id); - browser.buttonDown(); - browser.buttonUp(); - } - browser.clickWithWait('//*[contains(@class, "red-ui-notification")]/div/button[2]'); - browser.pause(1000); - } -} - -module.exports = completeNode; diff --git a/test/editor/pageobjects/nodes/core/common/25-catch_page.js b/test/editor/pageobjects/nodes/core/common/25-catch_page.js deleted file mode 100644 index 89bcb7f1e..000000000 --- a/test/editor/pageobjects/nodes/core/common/25-catch_page.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function catchNode(id) { - nodePage.call(this, id); -} - -util.inherits(catchNode, nodePage); - -catchNode.prototype.setScope = function (scope) { - if (scope) { - browser.selectWithWait('#node-input-scope-select', 'target'); - browser.clickWithWait('//*[@id="node-input-catch-target-select"]'); - browser.pause(1000); - if (Array.isArray(scope)) { - for (var i = 0; i < scope.length; i++) { - browser.moveToObject(scope[i].id); - browser.buttonDown(); - browser.buttonUp(); - } - } else { - browser.moveToObject(scope.id); - browser.buttonDown(); - browser.buttonUp(); - } - browser.clickWithWait('//*[contains(@class, "red-ui-notification")]/div/button[2]'); - browser.pause(1000); - } -} - -module.exports = catchNode; diff --git a/test/editor/pageobjects/nodes/core/common/25-status_page.js b/test/editor/pageobjects/nodes/core/common/25-status_page.js deleted file mode 100644 index 6de0fcde7..000000000 --- a/test/editor/pageobjects/nodes/core/common/25-status_page.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function statusNode(id) { - nodePage.call(this, id); -} - -util.inherits(statusNode, nodePage); - -statusNode.prototype.setScope = function (scope) { - if (scope) { - browser.selectWithWait('#node-input-scope-select', 'target'); - browser.clickWithWait('//*[@id="node-input-status-target-select"]'); - browser.pause(1000); - if (Array.isArray(scope)) { - for (var i = 0; i < scope.length; i++) { - browser.moveToObject(scope[i].id); - browser.buttonDown(); - browser.buttonUp(); - } - } else { - browser.moveToObject(scope.id); - browser.buttonDown(); - browser.buttonUp(); - } - browser.clickWithWait('//*[contains(@class, "red-ui-notification")]/div/button[2]'); - browser.pause(1000); - } -} - -module.exports = statusNode; diff --git a/test/editor/pageobjects/nodes/core/common/90-comment_page.js b/test/editor/pageobjects/nodes/core/common/90-comment_page.js deleted file mode 100644 index 2687dacfe..000000000 --- a/test/editor/pageobjects/nodes/core/common/90-comment_page.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function commentNode(id) { - nodePage.call(this, id); -} - -util.inherits(commentNode, nodePage); - -module.exports = commentNode; diff --git a/test/editor/pageobjects/nodes/core/function/10-function_page.js b/test/editor/pageobjects/nodes/core/function/10-function_page.js deleted file mode 100644 index c1bb80a80..000000000 --- a/test/editor/pageobjects/nodes/core/function/10-function_page.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var keyPage = require("../../../util/key_page"); - -function functionNode(id) { - nodePage.call(this, id); -} - -util.inherits(functionNode, nodePage); - -functionNode.prototype.setFunction = function (func) { - browser.clickWithWait('#node-input-func-editor'); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); - browser.keys(func); - // Delete the unnecessary code that ace editor does the autocompletion. - browser.keys(keyPage.selectToEnd()); - browser.keys(['Delete']); - // Need to wait until ace editor correctly checks the syntax. - browser.pause(300); -} - -module.exports = functionNode; diff --git a/test/editor/pageobjects/nodes/core/function/10-switch_page.js b/test/editor/pageobjects/nodes/core/function/10-switch_page.js deleted file mode 100644 index a04014063..000000000 --- a/test/editor/pageobjects/nodes/core/function/10-switch_page.js +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function switchNode(id) { - nodePage.call(this, id); -} - -util.inherits(switchNode, nodePage); - -var vtType = { - "msg": 1, - "flow": 2, - "global": 3, - "str": 4, - "num": 5, - "jsonata": 6, - "env": 7, - "prev": 8 -}; - -function setT(t, index) { - browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t); -} - -function setV(v, vt, index) { - if (vt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[vt] + ']'); - } - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', v); - } -} - -function setBetweenV(v, vt, v2, v2t, index) { - if (vt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[vt] + ']'); - } - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v); - } - if (v2t) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[v2t] + ']'); - } - if (v2) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div/div[1]/input', v2); - } -} - -function setSequenceV(v, vt, index) { - var sType = { - "flow": 1, - "global": 2, - "num": 3, - "jsonata": 4, - "env": 5, - }; - - if (vt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + sType[vt] + ']'); - } - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v); - } -} - -switchNode.prototype.ruleEqual = function (v, vt, index) { - index = index || 1; - setT('eq', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleNotEqual = function (v, vt, index) { - index = index || 1; - setT('neq', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleLessThan = function (v, vt, index) { - index = index || 1; - setT('lt', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleLessThanOrEqual = function (v, vt, index) { - index = index || 1; - setT('lte', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleGreaterThan = function (v, vt, index) { - index = index || 1; - setT('gt', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleGreaterThanOrEqual = function (v, vt, index) { - index = index || 1; - setT('gte', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleHasKey = function (v, vt, index) { - index = index || 1; - setT('hask', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleIsBetween = function (v, vt, v2, v2t, index) { - index = index || 1; - setT('btwn', index); - setBetweenV(v, vt, v2, v2t, index); -} - -switchNode.prototype.ruleContains = function (v, vt, index) { - index = index || 1; - setT('cont', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleMatchesRegex = function (v, vt, index) { - index = index || 1; - setT('regex', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleIsTrue = function (index) { - index = index || 1; - setT('true', index); -} - -switchNode.prototype.ruleIsFalse = function (index) { - index = index || 1; - setT('false', index); -} - -switchNode.prototype.ruleIsNull = function (index) { - index = index || 1; - setT('null', index); -} - -switchNode.prototype.ruleIsNotNull = function (index) { - index = index || 1; - setT('nnull', index); -} - -switchNode.prototype.ruleIsOfType = function (t, index) { - index = index || 1; - setT('istype', index); - - var tType = { - "str": 1, - "num": 2, - "boolean": 3, - "array": 4, - "buffer": 5, - "object": 6, - "json": 7, - "undefined": 8, - "null": 9 - }; - - if (t) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + tType[t] + ']'); - } -} - -switchNode.prototype.ruleIsEmpty = function (index) { - index = index || 1; - setT('empty', index); -} - -switchNode.prototype.ruleIsNotEmpty = function (index) { - index = index || 1; - setT('nempty', index); -} - -switchNode.prototype.ruleHead = function (v, vt, index) { - index = index || 1; - setT('head', index); - setSequenceV(v, vt, index); -} - -switchNode.prototype.ruleIndexBetween = function (v, vt, v2, v2t, index) { - index = index || 1; - setT('index', index); - setBetweenV(v, vt, v2, v2t, index); -} - -switchNode.prototype.ruleTail = function (v, vt, index) { - index = index || 1; - setT('tail', index); - setSequenceV(v, vt, index); -} - -switchNode.prototype.ruleJSONataExp = function (v, index) { - index = index || 1; - setT('jsonata_exp', index); - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v); - } -} - -switchNode.prototype.ruleOtherwise = function (index) { - index = index || 1; - setT('else', index); -} - -switchNode.prototype.addRule = function () { - browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a'); -} - -module.exports = switchNode; diff --git a/test/editor/pageobjects/nodes/core/function/15-change_page.js b/test/editor/pageobjects/nodes/core/function/15-change_page.js deleted file mode 100644 index eb26f48aa..000000000 --- a/test/editor/pageobjects/nodes/core/function/15-change_page.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function changeNode(id) { - nodePage.call(this, id); -} - -util.inherits(changeNode, nodePage); - -var totType = { - "msg": 1, - "flow": 2, - "global": 3, - "str": 4, - "num": 5, - "bool": 6, - "json": 7, - "bin": 8, - "date": 9, - "jsonata": 10, - "env": 11, -}; - -var ptType = { - "msg": 1, - "flow": 2, - "global": 3, -}; - -function setT(t, index) { - browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t); -} - -// It is better to create a function whose input value is the object type in the future, -changeNode.prototype.ruleSet = function (p, pt, to, tot, index) { - index = index || 1; - setT('set', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } - if (tot) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[tot] + ']'); - } - if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/div/input', to); - } -} - -changeNode.prototype.ruleChange = function (p, pt, from, fromt, to, tot, index) { - index = index || 1; - setT('change', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } - if (fromt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']'); - } - if (from) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[1]/div[2]/div[1]/input', from); - } - if (tot) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[2]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']'); - } - if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[2]/div[2]/div[1]/input', to); - } -} - -changeNode.prototype.ruleDelete = function (p, pt, index) { - index = index || 1; - setT('delete', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } -} - -changeNode.prototype.ruleMove = function (p, pt, to, tot, index) { - index = index || 1; - setT('move', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } - if (tot) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']'); - } - if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/div/input', to); - } -} - -changeNode.prototype.addRule = function () { - browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a'); -} - -module.exports = changeNode; diff --git a/test/editor/pageobjects/nodes/core/function/16-range_page.js b/test/editor/pageobjects/nodes/core/function/16-range_page.js deleted file mode 100644 index 230929995..000000000 --- a/test/editor/pageobjects/nodes/core/function/16-range_page.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function rangeNode(id) { - nodePage.call(this, id); -} - -util.inherits(rangeNode, nodePage); - -rangeNode.prototype.setAction = function(action) { - browser.selectWithWait('#node-input-action', action); -} - -rangeNode.prototype.setRange = function(minin, maxin, minout, maxout) { - browser.setValue('#node-input-minin', minin); - browser.setValue('#node-input-maxin', maxin); - browser.setValue('#node-input-minout', minout); - browser.setValue('#node-input-maxout', maxout); -} - -module.exports = rangeNode; diff --git a/test/editor/pageobjects/nodes/core/function/80-template_page.js b/test/editor/pageobjects/nodes/core/function/80-template_page.js deleted file mode 100644 index bf1786ba2..000000000 --- a/test/editor/pageobjects/nodes/core/function/80-template_page.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var keyPage = require("../../../util/key_page"); - -function templateNode(id) { - nodePage.call(this, id); -} - -util.inherits(templateNode, nodePage); - -templateNode.prototype.setSyntax = function (syntax) { - browser.selectWithWait('#node-input-syntax', syntax); -} - -templateNode.prototype.setFormat = function (format) { - browser.selectWithWait('#node-input-format', format); -} - -templateNode.prototype.setTemplate = function (template) { - browser.clickWithWait('#node-input-template-editor'); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); - browser.keys(template); - browser.keys(keyPage.selectToEnd()); - browser.keys(['Delete']); - // Need to wait until ace editor correctly checks the syntax. - browser.pause(300); -} - -module.exports = templateNode; diff --git a/test/editor/pageobjects/nodes/core/function/89-delay_page.js b/test/editor/pageobjects/nodes/core/function/89-delay_page.js deleted file mode 100644 index 3604beb67..000000000 --- a/test/editor/pageobjects/nodes/core/function/89-delay_page.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function delayNode(id) { - nodePage.call(this, id); -} - -util.inherits(delayNode, nodePage); - -delayNode.prototype.setTimeout = function (timeout) { - browser.setValue('//*[@id="node-input-timeout"]', timeout); -} - -module.exports = delayNode; diff --git a/test/editor/pageobjects/nodes/core/function/89-trigger_page.js b/test/editor/pageobjects/nodes/core/function/89-trigger_page.js deleted file mode 100644 index 5d24de380..000000000 --- a/test/editor/pageobjects/nodes/core/function/89-trigger_page.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function triggerNode(id) { - nodePage.call(this, id); -} - -util.inherits(triggerNode, nodePage); - -triggerNode.prototype.setSend = function (send, sendt) { - var sendType = { - "flow": 1, - "global": 2, - "str": 3, - "num": 4, - "bool": 5, - "json": 6, - "bin": 7, - "date": 8, - "env": 9, - "pay": 10, - "nul": 11 - }; - - if (sendt) { - browser.clickWithWait('//*[@id="dialog-form"]/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + sendType[sendt] + ']'); - } - if (send) { - browser.setValue('//*[@id="dialog-form"]/div[1]/div/div[1]/input', send); - } -} - -triggerNode.prototype.setDuration = function (duration, units) { - browser.setValue('//*[@id="node-input-duration"]', duration); - if (units) { - browser.selectWithWait('//*[@id="node-input-units"]', units); - } -} - -triggerNode.prototype.setThenSend = function (thenSend, thenSendt) { - var thenSendType = { - "flow": 1, - "global": 2, - "str": 3, - "num": 4, - "bool": 5, - "json": 6, - "bin": 7, - "date": 8, - "env": 9, - "pay": 10, - "payl": 11, - "nul": 12 - }; - - if (thenSendt) { - browser.clickWithWait('//*[@id="dialog-form"]/div[5]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + thenSendType[thenSendt] + ']'); - } - if (thenSend) { - browser.setValue('//*[@id="dialog-form"]/div[5]/div/div[1]/input', thenSend); - } -} - -module.exports = triggerNode; diff --git a/test/editor/pageobjects/nodes/core/function/90-exec_page.js b/test/editor/pageobjects/nodes/core/function/90-exec_page.js deleted file mode 100644 index 69b8b6c9a..000000000 --- a/test/editor/pageobjects/nodes/core/function/90-exec_page.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function execNode(id) { - nodePage.call(this, id); -} - -util.inherits(execNode, nodePage); - -execNode.prototype.setCommand = function (command) { - browser.setValue('//*[@id="node-input-command"]', command); -} - -execNode.prototype.setAppend = function (checkbox) { - if (browser.isSelected('#node-input-addpay') !== checkbox) { - browser.click('#node-input-addpay'); - } -} - -module.exports = execNode; diff --git a/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js b/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js deleted file mode 100644 index 4bdd92336..000000000 --- a/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var mqttBrokerNode = {}; - -mqttBrokerNode.setServer = function (broker, port) { - browser.setValue('#node-config-input-broker', broker); - port = port ? port : 1883; - browser.setValue('#node-config-input-port', port); -}; - -mqttBrokerNode.clickOk = function () { - browser.clickWithWait('#node-config-dialog-ok'); - // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 10000, true); -}; - -mqttBrokerNode.edit = function () { - browser.waitForVisible('#node-input-lookup-broker'); - browser.click('#node-input-lookup-broker'); - // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 10000); -}; - -function mqttInNode(id) { - nodePage.call(this, id); -} - -util.inherits(mqttInNode, nodePage); - -mqttInNode.prototype.setTopic = function (topic) { - browser.setValue('#node-input-topic', topic); -}; - -mqttInNode.prototype.setQoS = function (qos) { - browser.selectWithWait('#node-input-qos', qos); -}; - -mqttInNode.prototype.mqttBrokerNode = mqttBrokerNode; -module.exports.mqttInNode = mqttInNode; - -function mqttOutNode(id) { - nodePage.call(this, id); -} - -util.inherits(mqttOutNode, nodePage); - -mqttOutNode.prototype.setTopic = function (topic) { - browser.setValue('#node-input-topic', topic); -}; - -mqttOutNode.prototype.setRetain = function (retain) { - browser.selectWithWait('#node-input-retain', retain); -}; - -mqttOutNode.prototype.mqttBrokerNode = mqttBrokerNode; -module.exports.mqttOutNode = mqttOutNode; diff --git a/test/editor/pageobjects/nodes/core/network/21-httpin_page.js b/test/editor/pageobjects/nodes/core/network/21-httpin_page.js deleted file mode 100644 index 9454ef034..000000000 --- a/test/editor/pageobjects/nodes/core/network/21-httpin_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function httpInNode(id) { - nodePage.call(this, id); -} - -util.inherits(httpInNode, nodePage); - -httpInNode.prototype.setMethod = function(method) { - browser.selectWithWait('#node-input-method', method); -} - -httpInNode.prototype.setUrl = function(url) { - browser.setValue('#node-input-url', url); -} - -module.exports = httpInNode; diff --git a/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js b/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js deleted file mode 100644 index b2ca812e3..000000000 --- a/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function httpRequestNode(id) { - nodePage.call(this, id); -} - -util.inherits(httpRequestNode, nodePage); - -httpRequestNode.prototype.setUrl = function(url) { - browser.setValue('#node-input-url', url); -} - -httpRequestNode.prototype.setMethod = function(method) { - browser.selectWithWait('#node-input-method', method); -} - -httpRequestNode.prototype.setReturn = function(ret) { - browser.selectWithWait('#node-input-ret', ret); -} - -module.exports = httpRequestNode; diff --git a/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js b/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js deleted file mode 100644 index 7fb1886ce..000000000 --- a/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function httpResponseNode(id) { - nodePage.call(this, id); -} - -util.inherits(httpResponseNode, nodePage); - -module.exports = httpResponseNode; diff --git a/test/editor/pageobjects/nodes/core/network/22-websocket_page.js b/test/editor/pageobjects/nodes/core/network/22-websocket_page.js deleted file mode 100644 index 8f7dc261e..000000000 --- a/test/editor/pageobjects/nodes/core/network/22-websocket_page.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var websocketListenerNode = {}; - -websocketListenerNode.setPath = function (path) { - browser.setValue('#node-config-input-path', path); -}; - -websocketListenerNode.setSendReceive = function (wholemsg) { - browser.selectWithWait('#node-config-input-wholemsg', wholemsg); -}; - -websocketListenerNode.clickOk = function () { - browser.clickWithWait('#node-config-dialog-ok'); - // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 10000, true); -}; - -websocketListenerNode.edit = function () { - browser.waitForVisible('#node-input-lookup-server'); - browser.click('#node-input-lookup-server'); - // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 10000); -}; - -var websocketClientNode = {}; - -websocketClientNode.setPath = function (path) { - browser.setValue('#node-config-input-path', path); -}; - -websocketClientNode.setSendReceive = function (wholemsg) { - browser.selectWithWait('#node-config-input-wholemsg', wholemsg); -}; - -websocketClientNode.clickOk = function () { - browser.clickWithWait('#node-config-dialog-ok'); - // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 10000, true); -}; - -websocketClientNode.edit = function () { - browser.waitForVisible('#node-input-lookup-client'); - browser.click('#node-input-lookup-client'); - // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 10000); -}; - -function websocketInNode(id) { - nodePage.call(this, id); -} - -util.inherits(websocketInNode, nodePage); - -websocketInNode.prototype.setType = function (type) { - browser.selectWithWait('#node-input-mode', type); -}; - -websocketInNode.prototype.websocketListenerNode = websocketListenerNode; -websocketInNode.prototype.websocketClientNode = websocketClientNode; -module.exports.websocketInNode = websocketInNode; - -function websocketOutNode(id) { - nodePage.call(this, id); -} - -util.inherits(websocketOutNode, nodePage); - -websocketOutNode.prototype.setType = function (type) { - browser.selectWithWait('#node-input-mode', type); -}; - -websocketOutNode.prototype.websocketListenerNode = websocketListenerNode; -websocketOutNode.prototype.websocketClientNode = websocketClientNode; -module.exports.websocketOutNode = websocketOutNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js b/test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js deleted file mode 100644 index e4bc9502c..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function csvNode(id) { - nodePage.call(this, id); -} - -util.inherits(csvNode, nodePage); - -csvNode.prototype.setColumns = function (columns) { - browser.setValue('#node-input-temp', columns); -} - -csvNode.prototype.setSkipLines = function (skip) { - browser.setValue('#node-input-skip', skip); -} - -csvNode.prototype.setFirstRow4Names = function (checkbox) { - if (browser.isSelected('#node-input-hdrin') !== checkbox) { - browser.click('#node-input-hdrin'); - } -} - -csvNode.prototype.setOutput = function (output) { - browser.selectWithWait('#node-input-multi', output); -} - -csvNode.prototype.setIncludeRow = function (checkbox) { - if (browser.isSelected('#node-input-hdrout') !== checkbox) { - browser.click('#node-input-hdrout'); - } -} - -module.exports = csvNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js b/test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js deleted file mode 100644 index 243e4c905..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function htmlNode(id) { - nodePage.call(this, id); -} - -util.inherits(htmlNode, nodePage); - -htmlNode.prototype.setSelector = function (tag) { - browser.setValue('#node-input-tag', tag); -} - -module.exports = htmlNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js b/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js deleted file mode 100644 index e0b31dd36..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function jsonNode(id) { - nodePage.call(this, id); -} - -util.inherits(jsonNode, nodePage); - -jsonNode.prototype.setAction = function (action) { - browser.setValue('node-input-action', action); -} - -jsonNode.prototype.setProperty = function (property) { - browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); -} - -module.exports = jsonNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js b/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js deleted file mode 100644 index 696ec59cb..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function xmlNode(id) { - nodePage.call(this, id); -} - -util.inherits(xmlNode, nodePage); - -xmlNode.prototype.setAction = function (action) { - browser.setValue('node-input-action', action); -} - -xmlNode.prototype.setProperty = function (property) { - browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); -} - -module.exports = xmlNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js b/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js deleted file mode 100644 index 1002f3eb4..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function yamlNode(id) { - nodePage.call(this, id); -} - -util.inherits(yamlNode, nodePage); - -yamlNode.prototype.setAction = function (action) { - browser.setValue('node-input-action', action); -} - -yamlNode.prototype.setProperty = function (property) { - browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); -} - -module.exports = yamlNode; diff --git a/test/editor/pageobjects/nodes/core/sequence/17-split_page.js b/test/editor/pageobjects/nodes/core/sequence/17-split_page.js deleted file mode 100644 index 8fc32ab1e..000000000 --- a/test/editor/pageobjects/nodes/core/sequence/17-split_page.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function splitNode(id) { - nodePage.call(this, id); -} - -util.inherits(splitNode, nodePage); - -splitNode.prototype.setSplitUsing = function (splt, spltt) { - var spltType = { - "str": 1, - "bin": 2, - "len": 3 - }; - - browser.clickWithWait('//*[@id="dialog-form"]/div[3]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + spltType[spltt] + ']'); - browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', splt); -}; - -module.exports.splitNode = splitNode; - -function joinNode(id) { - nodePage.call(this, id); -} - -util.inherits(joinNode, nodePage); - -module.exports.joinNode = joinNode; diff --git a/test/editor/pageobjects/nodes/core/sequence/19-batch_page.js b/test/editor/pageobjects/nodes/core/sequence/19-batch_page.js deleted file mode 100644 index 0d7b13028..000000000 --- a/test/editor/pageobjects/nodes/core/sequence/19-batch_page.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function batchNode(id) { - nodePage.call(this, id); -} - -batchNode.prototype.setMode = function (mode) { - browser.selectWithWait('#node-input-mode', mode); -} - -batchNode.prototype.setCount = function (count) { - browser.setValue('#node-input-count', count); -} - -batchNode.prototype.setOverlap = function (overlap) { - browser.setValue('#node-input-overlap', overlap); -} - -util.inherits(batchNode, nodePage); - -module.exports = batchNode; diff --git a/test/editor/pageobjects/nodes/core/storage/10-filein_page.js b/test/editor/pageobjects/nodes/core/storage/10-filein_page.js deleted file mode 100644 index 942ea63d8..000000000 --- a/test/editor/pageobjects/nodes/core/storage/10-filein_page.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function fileInNode(id) { - nodePage.call(this, id); -} - -util.inherits(fileInNode, nodePage); - -var formatType = { - "utf8": 1, - "lines": 2, - "": 3, // a single Buffer object - "stream": 4 -}; - -fileInNode.prototype.setFilename = function(filename) { - browser.setValue('#node-input-filename', filename); -} - -fileInNode.prototype.setOutput = function(format) { - browser.clickWithWait('#node-input-format'); - var formatTypeXPath = '//*[@id="node-input-format"]/option[' + formatType[format] + ']'; - browser.clickWithWait(formatTypeXPath); -} - -module.exports = fileInNode; diff --git a/test/editor/pageobjects/nodes/node_page.js b/test/editor/pageobjects/nodes/node_page.js deleted file mode 100644 index 03e734cab..000000000 --- a/test/editor/pageobjects/nodes/node_page.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -function Node(id) { - this.id = '//*[@id="' + id + '"]'; -} - -Node.prototype.edit = function () { - browser.waitForVisible(this.id); - browser.moveToObject(this.id); - browser.buttonDown(); - browser.buttonUp(); - browser.keys(['Enter']); - // Wait until an edit dialog opens. - browser.waitForVisible('#node-dialog-ok', 10000); -} - -Node.prototype.clickOk = function () { - browser.clickWithWait('#node-dialog-ok'); - // Wait untile an edit dialog closes. - browser.waitForVisible('#node-dialog-ok', 10000, true); - browser.pause(50); -} - -Node.prototype.connect = function (targetNode, port) { - port = port || 1; - var outputPort = this.id + '/*[@class="red-ui-flow-port-output"][' + port + ']'; - var inputPort = targetNode.id + '/*[@class="red-ui-flow-port-input"]'; - browser.dragAndDrop(outputPort, inputPort); -} - -Node.prototype.clickLeftButton = function () { - browser.moveToObject(this.id + '/*[@class="red-ui-flow-node-button"]'); - browser.buttonDown(); - browser.buttonUp(); -} - -module.exports = Node; diff --git a/test/editor/pageobjects/nodes/nodefactory_page.js b/test/editor/pageobjects/nodes/nodefactory_page.js deleted file mode 100644 index 008ecc625..000000000 --- a/test/editor/pageobjects/nodes/nodefactory_page.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var injectNode = require('./core/common/20-inject_page'); -var debugNode = require('./core/common/21-debug_page'); -var completeNode = require('./core/common/24-complete_page'); -var catchNode = require('./core/common/25-catch_page'); -var statusNode = require('./core/common/25-status_page'); -var commentNode = require('./core/common/90-comment_page'); -var functionNode = require('./core/function/10-function_page'); -var switchNode = require('./core/function/10-switch_page'); -var changeNode = require('./core/function/15-change_page'); -var rangeNode = require('./core/function/16-range_page'); -var templateNode = require('./core/function/80-template_page'); -var delayNode = require('./core/function/89-delay_page'); -var triggerNode = require('./core/function/89-trigger_page'); -var execNode = require('./core/function/90-exec_page'); -var mqttInNode = require('./core/network/10-mqtt_page').mqttInNode; -var mqttOutNode = require('./core/network/10-mqtt_page').mqttOutNode; -var httpInNode = require('./core/network/21-httpin_page'); -var httpResponseNode = require('./core/network/21-httpresponse_page'); -var httpRequestNode = require('./core/network/21-httprequest_page'); -var websocketInNode = require('./core/network/22-websocket_page').websocketInNode; -var websocketOutNode = require('./core/network/22-websocket_page').websocketOutNode; -var splitNode = require('./core/sequence/17-split_page').splitNode; -var joinNode = require('./core/sequence/17-split_page').joinNode; -var batchNode = require('./core/sequence/19-batch_page'); -var csvNode = require('./core/parsers/70-CSV_page'); -var htmlNode = require('./core/parsers/70-HTML_page'); -var jsonNode = require('./core/parsers/70-JSON_page'); -var xmlNode = require('./core/parsers/70-XML_page'); -var yamlNode = require('./core/parsers/70-YAML_page'); -var fileInNode = require('./core/storage/10-filein_page'); - -var nodeCatalog = { - // common - "inject": injectNode, - "debug": debugNode, - "complete": completeNode, - "catch": catchNode, - "status": statusNode, - "comment": commentNode, - // function - "function": functionNode, - "switch": switchNode, - "change": changeNode, - "range": rangeNode, - "template": templateNode, - "delay": delayNode, - "trigger": triggerNode, - "exec": execNode, - // network - "mqttIn": mqttInNode, - "mqttOut": mqttOutNode, - "httpIn": httpInNode, - "httpResponse": httpResponseNode, - "httpRequest": httpRequestNode, - "websocketIn": websocketInNode, - "websocketOut": websocketOutNode, - // sequence - "split": splitNode, - "join": joinNode, - "batch": batchNode, - // parser - "csv": csvNode, - "html": htmlNode, - "json": jsonNode, - "xml": xmlNode, - "yaml": yamlNode, - // storage - "fileIn": fileInNode -}; - -function create(type, id) { - var Node = nodeCatalog[type]; - return new Node(id); -} - -module.exports = { - create: create, -}; diff --git a/test/editor/pageobjects/util/key_page.js b/test/editor/pageobjects/util/key_page.js deleted file mode 100644 index 497a8a141..000000000 --- a/test/editor/pageobjects/util/key_page.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var os = require("os"); - -var shortCutKeyMap = { - "selectAll": ['Control', 'a', 'a', 'Control'], - "selectToEnd": ['Control', 'Shift', 'End', 'Shift', 'Control'], -}; - -var shortCutKeyMapForMac = { - "selectAll": ['Command', 'a', 'a', 'Command'], - "selectToEnd": ['Command', 'Shift', 'ArrowDown', 'Shift', 'Command'], -}; - -function getShortCutKey(type) { - if (process.env.BROWSERSTACK) { - if (browser.desiredCapabilities.os === 'OS X') { - return shortCutKeyMapForMac[type]; - } - return shortCutKeyMap[type]; - } - if (os.type() === 'Darwin') { - return shortCutKeyMapForMac[type]; - } - return shortCutKeyMap[type]; -} - -function selectAll() { - var key = getShortCutKey('selectAll'); - return key; -} - -function selectToEnd() { - var key = getShortCutKey('selectToEnd'); - return key; -} - -module.exports = { - selectAll: selectAll, - selectToEnd: selectToEnd, -}; diff --git a/test/editor/pageobjects/util/spec_util_page.js b/test/editor/pageobjects/util/spec_util_page.js deleted file mode 100644 index f64743664..000000000 --- a/test/editor/pageobjects/util/spec_util_page.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -function pause(msec) { - browser.pause(msec); -} - - module.exports = { - pause: pause, - }; diff --git a/test/editor/pageobjects/util/util_page.js b/test/editor/pageobjects/util/util_page.js deleted file mode 100644 index 3a764eb93..000000000 --- a/test/editor/pageobjects/util/util_page.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var waitTime = 5000; - -function repeatUntilSuccess(operation, args) { - // Wait at most 10 seconds. - for (var i = 0; i < 200; i++) { - try { - var ret = operation(args); - return ret; - } catch (e) { - if (i === 199) { - console.trace(); - throw e; - } - browser.pause(50); - } - } -} - -function init() { - browser.addCommand("clickWithWait", function(selector) { - try { - // This is necessary because there is a case that the target may exist but still moving. - browser.pause(50); - browser.waitForVisible(selector); - - var ret = repeatUntilSuccess(function(selector) { - return browser.click(selector); - }, selector); - return ret; - } catch (e) { - console.trace(); - throw e; - } - }, false); - - browser.addCommand("getTextWithWait", function(selector) { - try { - browser.waitForExist(selector); - browser.waitForValue(selector); - - var ret = repeatUntilSuccess(function(selector) { - return browser.getText(selector); - }, selector); - return ret; - } catch (e) { - console.trace(); - throw e; - } - }, false); - - browser.addCommand("selectWithWait", function(selector, value) { - try { - browser.waitForVisible(selector, waitTime); - - var ret = repeatUntilSuccess(function(args) { - return browser.selectByValue(args[0], args[1]); - }, [selector, value.toString()]); - return ret; - } catch (e) { - console.trace(); - throw e; - } - }, false); -} - - module.exports = { - init: init, - }; diff --git a/test/editor/specs/editor/workspace_uispec.js b/test/editor/specs/editor/workspace_uispec.js deleted file mode 100644 index 15de1a044..000000000 --- a/test/editor/specs/editor/workspace_uispec.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require("should"); -var fs = require('fs-extra'); - -var helper = require("../../editor_helper"); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); - - -describe('Workspace', function() { - beforeEach(function() { - workspace.init(); - }); - - before(function() { - helper.startServer(); - }); - - after(function() { - helper.stopServer(); - }); - - it('should have a right title', function () { - browser.getTitle().should.startWith('Node-RED'); - }); - - it('should output a timestamp', function() { - var injectNode = workspace.addNode("inject"); - var debugNode = workspace.addNode("debug"); - injectNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.within(1500000000000, 3000000000000); - }); - -}); diff --git a/test/editor/specs/scenario/cookbook_dataformats_uispec.js b/test/editor/specs/scenario/cookbook_dataformats_uispec.js deleted file mode 100644 index 8321bd924..000000000 --- a/test/editor/specs/scenario/cookbook_dataformats_uispec.js +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('working with data formats', function () { - it('convert to/from JSON', function () { - var injectNode1 = workspace.addNode('inject'); - var jsonNode1 = workspace.addNode('json'); - var debugNode1 = workspace.addNode('debug'); - - injectNode1.edit(); - injectNode1.setPayload('str', '{"a":1}'); - injectNode1.clickOk(); - - jsonNode1.edit(); - jsonNode1.setProperty('payload'); - jsonNode1.clickOk(); - - injectNode1.connect(jsonNode1); - jsonNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject'); - var jsonNode2 = workspace.addNode('json'); - var debugNode2 = workspace.addNode('debug'); - - injectNode2.edit(); - injectNode2.setPayload('json', '{"a":1}'); - injectNode2.clickOk(); - - jsonNode2.edit(); - jsonNode2.setProperty('payload'); - jsonNode2.clickOk(); - - injectNode2.connect(jsonNode2); - jsonNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.eql('1'); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.eql('"{"a":1}"'); - }); - - it('convert to/from XML', function () { - var injectNode1 = workspace.addNode('inject', 0); - var templateNode1 = workspace.addNode('template', 200); - var xmlNode1 = workspace.addNode('xml', 400); - var debugNode1 = workspace.addNode('debug', 600); - - injectNode1.edit(); - injectNode1.setPayload('str', '{"a":1}'); - injectNode1.clickOk(); - - templateNode1.edit(); - templateNode1.setFormat('text'); - templateNode1.setSyntax('plain'); - templateNode1.setTemplate('' - + ' Nick' - + ' Dave' - + ' Reminder' - + ' Update the website' - + ''); - templateNode1.clickOk(); - - xmlNode1.edit(); - xmlNode1.setProperty('payload'); - xmlNode1.clickOk(); - - injectNode1.connect(templateNode1); - templateNode1.connect(xmlNode1); - xmlNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject'); - var xmlNode2 = workspace.addNode('xml'); - var debugNode2 = workspace.addNode('debug'); - - injectNode2.edit(); - injectNode2.setPayload('json', '{' - + ' "note": {' - + ' "$": { "priority": "high" },' - + ' "to": [ "Nick" ],' - + ' "from": [ "Dave" ],' - + ' "heading": [ "Reminder" ],' - + ' "body": [ "Update the website" ]' - + ' }' - + '}'); - injectNode2.clickOk(); - - xmlNode2.edit(); - xmlNode2.setProperty('payload'); - xmlNode2.clickOk(); - - injectNode2.connect(xmlNode2); - xmlNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.eql('object'); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.eql('"' - + '' - + 'Nick' - + 'Dave' - + 'Reminder' - + 'Update the website' - + '"'); - }); - - it('convert to/from YAML', function () { - var injectNode1 = workspace.addNode('inject', 0); - var templateNode1 = workspace.addNode('template', 200); - var yamlNode1 = workspace.addNode('yaml', 400); - var debugNode1 = workspace.addNode('debug', 600); - - injectNode1.edit(); - injectNode1.setPayload('str', '{"a":1}'); - injectNode1.clickOk(); - - templateNode1.edit(); - templateNode1.setFormat('yaml'); - templateNode1.setSyntax('plain'); - templateNode1.setTemplate('a: 1\n' - + 'b:\n' - + ' - 1\n' - + '- 2\n' - + '- 3'); - templateNode1.clickOk(); - - yamlNode1.edit(); - yamlNode1.setProperty('payload'); - yamlNode1.clickOk(); - - injectNode1.connect(templateNode1); - templateNode1.connect(yamlNode1); - yamlNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject'); - var yamlNode2 = workspace.addNode('yaml'); - var debugNode2 = workspace.addNode('debug'); - - injectNode2.edit(); - injectNode2.setPayload('json', '{"a":1, "b":[1,2,3]}'); - injectNode2.clickOk(); - - yamlNode2.edit(); - yamlNode2.setProperty('payload'); - yamlNode2.clickOk(); - - injectNode2.connect(yamlNode2); - yamlNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.eql([ '1', 'array[3]' ]); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.eql('"a: 1↵b:↵ - 1↵ - 2↵ - 3↵"'); - }); - - it('generate CSV output', function () { - var injectNode1 = workspace.addNode('inject', 0); - var changeNode1 = workspace.addNode('change', 200); - var csvNode1 = workspace.addNode('csv', 400); - var debugNode1 = workspace.addNode('debug', 600); - - changeNode1.edit(); - changeNode1.ruleSet('payload', 'msg', '{' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + '}', 'jsonata'); - changeNode1.clickOk(); - - csvNode1.edit(); - csvNode1.setColumns('a,b,c'); - csvNode1.clickOk(); - - injectNode1.connect(changeNode1); - changeNode1.connect(csvNode1); - csvNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject', 0, 80); - var changeNode2 = workspace.addNode('change', 200, 80); - var csvNode2 = workspace.addNode('csv', 400, 80); - var debugNode2 = workspace.addNode('debug', 600, 80); - - changeNode2.edit(); - changeNode2.ruleSet('payload', 'msg', '[' - + ' {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }, {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }, {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }, {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }' - + ']', 'jsonata'); - changeNode2.clickOk(); - - csvNode2.edit(); - csvNode2.setColumns('a,b,c'); - csvNode2.setIncludeRow(true); - csvNode2.clickOk(); - - injectNode2.connect(changeNode2); - changeNode2.connect(csvNode2); - csvNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.match(/^"([1-9]?[0-9],){2}[1-9]?[0-9]↵"$/); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.match(/^"(([1-9]?[0-9],){2}[1-9]?[0-9]↵){4}"$/); - }); - - it('parse CSV input', function () { - var injectNode = workspace.addNode('inject'); - var templateNode = workspace.addNode('template'); - var csvNode = workspace.addNode('csv'); - var debugNode = workspace.addNode('debug'); - - templateNode.edit(); - templateNode.setFormat('handlebars'); - templateNode.setSyntax('mustache'); - templateNode.setTemplate('# This is some random data\n' - + 'a,b,c\n' - + '80,18,2\n' - + '52,36,10\n' - + '91,18,61\n' - + '32,47,65'); - templateNode.clickOk(); - - csvNode.edit(); - csvNode.setSkipLines(1); - csvNode.setFirstRow4Names(true); - csvNode.setOutput('mult'); - csvNode.clickOk(); - - injectNode.connect(templateNode); - templateNode.connect(csvNode); - csvNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql([ 'object', 'object', 'object', 'object' ]); - }); - - it('simple GET request', function () { - var injectNode = workspace.addNode('inject'); - var httpRequestNode = workspace.addNode('httpRequest'); - var htmlNode = workspace.addNode('html'); - var debugNode = workspace.addNode('debug'); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - httpRequestNode.setUrl('https://nodered.org'); - httpRequestNode.clickOk(); - - htmlNode.edit(); - htmlNode.setSelector('.node-red-latest-version'); - htmlNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(htmlNode); - htmlNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.match(/^"v[0-9]+\.[0-9]+\.[0-9]"$/); - }); - - it('split text into one message per line', function () { - var injectNode = workspace.addNode('inject'); - var templateNode = workspace.addNode('template'); - var splitNode = workspace.addNode('split'); - var changeNode = workspace.addNode('change'); - var joinNode = workspace.addNode('join'); - var debugNode = workspace.addNode('debug'); - - templateNode.edit(); - templateNode.setFormat('handlebars'); - templateNode.setSyntax('mustache'); - templateNode.setTemplate('one\ntwo\nthree\nfour\nfive'); - templateNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('payload', 'msg', '(parts.index+1) & ": " & payload', 'jsonata'); - changeNode.clickOk(); - - injectNode.connect(templateNode); - templateNode.connect(splitNode); - splitNode.connect(changeNode); - changeNode.connect(joinNode); - joinNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"1: one↵2: two↵3: three↵4: four↵5: five"'); - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_errorhandling_uispec.js b/test/editor/specs/scenario/cookbook_errorhandling_uispec.js deleted file mode 100644 index 5285c5c0f..000000000 --- a/test/editor/specs/scenario/cookbook_errorhandling_uispec.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('messages', function () { - it('trigger a flow when a node throws an error', function () { - var injectNode = workspace.addNode('inject'); - var functionNode = workspace.addNode('function'); - var catchNode = workspace.addNode('catch', 0 , 80); - var debugNode = workspace.addNode('debug'); - - functionNode.edit(); - functionNode.setFunction('node.error("an example error", msg);'); - functionNode.clickOk(); - - catchNode.edit(); - catchNode.setScope(functionNode); - catchNode.clickOk(); - - debugNode.edit(); - debugNode.setOutput('error'); - debugNode.clickOk(); - - injectNode.connect(functionNode); - catchNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql(['"an example error"', 'function']); - }); - - // skip this case since the flow outputs random results. - it.skip('automatically retry an action after an error'); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_flowcontrol_uispec.js b/test/editor/specs/scenario/cookbook_flowcontrol_uispec.js deleted file mode 100644 index 724d1c56f..000000000 --- a/test/editor/specs/scenario/cookbook_flowcontrol_uispec.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('flow control', function () { - it('trigger a flow whenever Node-RED starts', function () { - var injectNode = workspace.addNode('inject'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'Started!'); - injectNode.setOnce(true); - injectNode.clickOk(); - injectNode.connect(debugNode); - - debugTab.open(); - workspace.deploy(); - debugTab.getMessage().should.eql('"Started!"'); - }); - - it('trigger a flow at regular intervals', function () { - var injectNode = workspace.addNode('inject'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setRepeat('interval'); - injectNode.setRepeatInterval(1); - injectNode.clickOk(); - injectNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - specUtil.pause(1000); - var t1 = Number(debugTab.getMessage(1)); - t1.should.within(1500000000000, 3000000000000); - specUtil.pause(1000); - debugTab.getMessage(2).should.within(t1 + 900, 3000000000000); - }); - - // skip this case since it needs up to one minite. - it.skip('trigger a flow at a specific time'); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_httpendpoints_uispec.js b/test/editor/specs/scenario/cookbook_httpendpoints_uispec.js deleted file mode 100644 index 134498370..000000000 --- a/test/editor/specs/scenario/cookbook_httpendpoints_uispec.js +++ /dev/null @@ -1,572 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var helper = require("../../editor_helper"); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); - -var httpNodeRoot = "/api"; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('HTTP endpoints', function () { - it('create an HTTP endpoint', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Hello World!

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello World!').should.not.eql(-1); - }); - - it('handle query parameters passed to an HTTP endpoint', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-query"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Hello {{req.query.name}}!

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-query?name=Nick'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('handle url parameters in an HTTP endpoint', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-param/:name"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Hello {{req.params.name}}!

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-param/Dave'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Dave!').should.not.eql(-1); - }); - - it('access HTTP request headers', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-headers"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

User agent: {{req.headers.user-agent}}

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 100); - var changeNode = workspace.addNode("change"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", '{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}', "json"); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-headers'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Mozilla').should.not.eql(-1); - }); - - it('include data captured in another flow', function () { - var injectNodeTimestamp = workspace.addNode("inject"); - var changeNodeStore = workspace.addNode("change"); - - var httpInNode = workspace.addNode("httpIn", 0, 100); - var changeNodeCopy = workspace.addNode("change"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - injectNodeTimestamp.edit(); - injectNodeTimestamp.setPayload("date"); - injectNodeTimestamp.clickOk(); - - changeNodeStore.edit(); - changeNodeStore.ruleSet("timestamp", "flow", "payload", "msg"); - changeNodeStore.clickOk(); - - injectNodeTimestamp.connect(changeNodeStore); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-data"); - httpInNode.clickOk(); - - changeNodeCopy.edit(); - changeNodeCopy.ruleSet("timestamp", "msg", "timestamp", "flow"); - changeNodeCopy.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Time: {{ timestamp }}

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(changeNodeCopy); - changeNodeCopy.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNodeCheck = workspace.addNode("inject", 0, 300); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setMethod("GET"); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-data'); - httpRequestNode.clickOk(); - - injectNodeCheck.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNodeTimestamp.clickLeftButton(); - injectNodeCheck.clickLeftButton(); - var index = debugTab.getMessage().indexOf('Time: ') + 6; - debugTab.getMessage().substring(index, index + 13).should.within(1500000000000, 3000000000000); - }); - - it('serve JSON content', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var changeNode = workspace.addNode("change"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-json"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate('{ "Hello": "World" }'); - templateNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", "{}", "json", "1"); - changeNode.addRule(); - changeNode.ruleSet("headers.content-type", "msg", "application/json", "str", "2"); - changeNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(changeNode); - changeNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 200); - var httpRequestNode = workspace.addNode("httpRequest"); - var changeNodeCheck = workspace.addNode("change"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setMethod("GET"); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-json'); - httpRequestNode.clickOk(); - - changeNodeCheck.edit(); - changeNodeCheck.ruleSet("payload", "msg", "headers.content-type", "msg", "1"); - changeNodeCheck.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(changeNodeCheck); - changeNodeCheck.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - var messages = debugTab.getMessage(); - messages.indexOf('application/json').should.not.eql(-1); - }); - - it('serve a local file', function () { - var httpInNode = workspace.addNode("httpIn"); - var fileInNode = workspace.addNode("fileIn"); - var changeNode = workspace.addNode("change", 200, 100); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-file"); - httpInNode.clickOk(); - - fileInNode.edit(); - fileInNode.setFilename("test/resources/file-in-node/test.txt"); - fileInNode.setOutput(""); - fileInNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", "{}", "json"); - changeNode.addRule(); - changeNode.ruleSet("headers.content-type", "msg", "text/plain", "str", "2"); - changeNode.clickOk(); - - httpInNode.connect(fileInNode); - fileInNode.connect(changeNode); - changeNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 200); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-file'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Text file').should.not.eql(-1); - }); - - it('post raw data to a flow', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("post"); - httpInNode.setUrl("/hello-raw"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Hello {{ payload }}!

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("str", "Nick"); - injectNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-raw'); - httpRequestNode.setMethod("POST"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('post form data to a flow', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("post"); - httpInNode.setUrl("/hello-form"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Hello {{ payload.name }}!

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 100); - var changeNode = workspace.addNode("change"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("str", "name=Nick"); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", '{"content-type":"application/x-www-form-urlencoded"}', "json"); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-form'); - httpRequestNode.setMethod("POST"); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('post JSON data to a flow', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("post"); - httpInNode.setUrl("/hello-json"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

Hello {{ payload.name }}!

\n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 100); - var changeNode = workspace.addNode("change"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("json", '{"name":"Nick"}'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", '{"content-type":"application/json"}', "json"); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-json'); - httpRequestNode.setMethod("POST"); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('work with cookies', function () { - var httpInNodeFormat = workspace.addNode("httpIn"); - var functionNodeFormat = workspace.addNode("function", 240); - var templateNode = workspace.addNode("template", 400); - var httpResponseNode = workspace.addNode("httpResponse", 600); - - var httpInNodeAdd = workspace.addNode("httpIn", 0, 100); - var functionNodeAdd = workspace.addNode("function", 240); - var changeNode = workspace.addNode("change", 400); - - var httpInNodeClear = workspace.addNode("httpIn", 0, 200); - var functionNodeClear = workspace.addNode("function", 250); - - httpInNodeFormat.edit(); - httpInNodeFormat.setMethod("get"); - httpInNodeFormat.setUrl("/hello-cookie"); - httpInNodeFormat.clickOk(); - - functionNodeFormat.edit(); - functionNodeFormat.setFunction("msg.payload = JSON.stringify(msg.req.cookies,null,4);\nreturn msg;"); - functionNodeFormat.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate('\n\n\n

Cookies

\n

Add a cookieClear cookies

\n
{{ payload }}
\n\n'); - templateNode.clickOk(); - - httpInNodeFormat.connect(functionNodeFormat); - functionNodeFormat.connect(templateNode); - templateNode.connect(httpResponseNode); - - httpInNodeAdd.edit(); - httpInNodeAdd.setMethod("get"); - httpInNodeAdd.setUrl("/hello-cookie/add"); - httpInNodeAdd.clickOk(); - - functionNodeAdd.edit(); - functionNodeAdd.setFunction('msg.cookies = { };\n msg.cookies["demo-"+(Math.floor(Math.random()*1000))] = Date.now();\nreturn msg;'); - functionNodeAdd.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("statusCode", "msg", "302", "num"); - changeNode.addRule(); - changeNode.ruleSet("headers", "msg", "{}", "json", "2"); - changeNode.addRule(); - changeNode.ruleSet("headers.location", "msg", httpNodeRoot + "/hello-cookie", "str", "3"); - changeNode.clickOk(); - - httpInNodeAdd.connect(functionNodeAdd); - functionNodeAdd.connect(changeNode); - changeNode.connect(httpResponseNode); - - httpInNodeClear.edit(); - httpInNodeClear.setMethod("get"); - httpInNodeClear.setUrl("/hello-cookie/clear"); - httpInNodeClear.clickOk(); - - functionNodeClear.edit(); - functionNodeClear.setFunction("var cookieNames = Object.keys(msg.req.cookies).filter(function(cookieName) { return /^demo-/.test(cookieName);});\nmsg.cookies = {};\n\ncookieNames.forEach(function(cookieName) {\n msg.cookies[cookieName] = null;\n});\nreturn msg;\n"); - functionNodeClear.clickOk(); - - httpInNodeClear.connect(functionNodeClear); - functionNodeClear.connect(changeNode); - - workspace.deploy(); - // This case cannot be checked since http request node does not transfer cookies when redirected. - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_httprequests_uispec.js b/test/editor/specs/scenario/cookbook_httprequests_uispec.js deleted file mode 100644 index 797b06041..000000000 --- a/test/editor/specs/scenario/cookbook_httprequests_uispec.js +++ /dev/null @@ -1,300 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('HTTP requests', function () { - it('simple get request', function () { - var injectNode = workspace.addNode('inject'); - var httpRequestNode = workspace.addNode('httpRequest'); - var htmlNode = workspace.addNode('html'); - var debugNode = workspace.addNode('debug'); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - httpRequestNode.setUrl(helper.url()); - httpRequestNode.clickOk(); - - htmlNode.edit(); - htmlNode.setSelector('title'); - htmlNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(htmlNode); - htmlNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Node-RED"'); - }); - - it('set the URL of a request', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', helper.url()); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('url', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.containEql('Node-RED'); - }); - - it('set the URL of a request using a template', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'settings'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('query', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + '/{{{query}}}'); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.containEql('httpNodeRoot'); - }); - - it('set the query string parameters', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'Nick'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('query', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/set-query?q={{{query}}}'); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - // The code for confirmation starts from here. - var httpInNode = workspace.addNode('httpIn', 0, 200); - var templateNode = workspace.addNode('template'); - var httpResponseNode = workspace.addNode('httpResponse'); - - httpInNode.edit(); - httpInNode.setMethod('get'); - httpInNode.setUrl('/set-query'); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax('mustache'); - templateNode.setFormat('handlebars'); - templateNode.setTemplate('Hello {{req.query.q}}'); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello Nick"'); - }); - - it('get a parsed JSON response', function () { - var injectNode = workspace.addNode('inject'); - var changeNodeSetPost = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'json-response'); - injectNode.clickOk(); - - changeNodeSetPost.edit(); - changeNodeSetPost.ruleSet('post', 'msg', 'payload', 'msg'); - changeNodeSetPost.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - var url = helper.url() + httpNodeRoot + '/{{post}}'; - httpRequestNode.setUrl(url); - httpRequestNode.setReturn('obj'); - httpRequestNode.clickOk(); - - debugNode.edit(); - debugNode.setOutput('payload.title'); - debugNode.clickOk(); - - injectNode.connect(changeNodeSetPost); - changeNodeSetPost.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - // The code for confirmation starts from here. - var httpInNode = workspace.addNode('httpIn', 0, 200); - var templateNode = workspace.addNode('template'); - var changeNodeSetHeader = workspace.addNode('change'); - var httpResponseNode = workspace.addNode('httpResponse'); - - httpInNode.edit(); - httpInNode.setMethod('get'); - httpInNode.setUrl('/json-response'); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax('mustache'); - templateNode.setFormat('handlebars'); - templateNode.setTemplate('{"title": "Hello"}'); - templateNode.clickOk(); - - changeNodeSetHeader.edit(); - changeNodeSetHeader.ruleSet('headers', 'msg', '{"content-type":"application/json"}', 'json'); - changeNodeSetHeader.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(changeNodeSetHeader); - changeNodeSetHeader.connect(httpResponseNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello"'); - }); - - it('get a binary response', function () { - var injectNode = workspace.addNode('inject'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - httpRequestNode.setUrl(helper.url() + '/settings'); - httpRequestNode.setReturn('bin'); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - - debugTab.getMessage().should.eql(['123', '34', '104', '116', '116', '112', '78', '111', '100', '101']); - }); - - it('set a request header', function () { - var injectNode = workspace.addNode('inject'); - var functionNode = workspace.addNode('function'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - functionNode.edit(); - functionNode.setFunction('msg.payload = "data to post";\nreturn msg;'); - functionNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setMethod('POST'); - var url = helper.url() + httpNodeRoot + '/set-header'; - httpRequestNode.setUrl(url); - httpRequestNode.clickOk(); - - injectNode.connect(functionNode); - functionNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - // The code for confirmation starts from here. - var httpInNode = workspace.addNode('httpIn', 0, 200); - var templateNode = workspace.addNode('template'); - var httpResponseNode = workspace.addNode('httpResponse'); - - httpInNode.edit(); - httpInNode.setMethod('post'); - httpInNode.setUrl('/set-header'); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax('mustache'); - templateNode.setFormat('handlebars'); - templateNode.setTemplate('{{ payload }}'); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"data to post"'); - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_messages_uispec.js b/test/editor/specs/scenario/cookbook_messages_uispec.js deleted file mode 100644 index 78facbcda..000000000 --- a/test/editor/specs/scenario/cookbook_messages_uispec.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('messages', function () { - it('set a message property to a fixed value', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var debugNode = workspace.addNode('debug'); - - changeNode.edit(); - changeNode.ruleSet('payload', 'msg', 'Hello World!'); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello World!"'); - }); - - it('delete a message property', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var debugNode = workspace.addNode('debug'); - - changeNode.edit(); - changeNode.ruleDelete(); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('undefined'); - }); - - it('move a message property', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setTopic('Hello'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleMove('topic', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello"'); - }); - - it('map a property between different numeric ranges', function () { - var injectNode1 = workspace.addNode('inject'); - var injectNode2 = workspace.addNode('inject', 0, 100); - var injectNode3 = workspace.addNode('inject', 0, 200); - var rangeNode = workspace.addNode('range', 200, 100); - var debugNode = workspace.addNode('debug', 400); - - injectNode1.edit(); - injectNode1.setPayload('num', 0); - injectNode1.clickOk(); - injectNode2.edit(); - injectNode2.setPayload('num', 512); - injectNode2.clickOk(); - injectNode3.edit(); - injectNode3.setPayload('num', 1023); - injectNode3.clickOk(); - - rangeNode.edit(); - rangeNode.setAction('clamp'); - rangeNode.setRange(0, 1023, 0, 5); - rangeNode.clickOk(); - - injectNode1.connect(rangeNode); - injectNode2.connect(rangeNode); - injectNode3.connect(rangeNode); - rangeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage(1).should.eql('0'); - injectNode2.clickLeftButton(); - debugTab.getMessage(2).should.eql('2.5024437927663734'); - injectNode3.clickLeftButton(); - debugTab.getMessage(3).should.eql('5'); - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_mqtt_uispec.js b/test/editor/specs/scenario/cookbook_mqtt_uispec.js deleted file mode 100644 index b68170eb5..000000000 --- a/test/editor/specs/scenario/cookbook_mqtt_uispec.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require("should"); -var fs = require('fs-extra'); - -var helper = require("../../editor_helper"); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = "/api"; - -var mqttServer; -var mosca = require('mosca'); -var moscaSettings = { - port: parseInt(Math.random() * 16383 + 49152), - persistence: { - // Needs for retaining messages. - factory: mosca.persistence.Memory - } -}; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - browser.call(function () { - return new Promise(function (resolve, reject) { - mqttServer = new mosca.Server(moscaSettings, function (err) { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - }); - helper.startServer(); - }); - - after(function () { - browser.call(function () { - return new Promise(function (resolve, reject) { - mqttServer.close(function () { - resolve(); - }); - }); - }); - helper.stopServer(); - }); - - describe('MQTT', function () { - it('Add an MQTT broker to prepare for UI test', function () { - var mqttOutNode = workspace.addNode("mqttOut"); - - mqttOutNode.edit(); - mqttOutNode.mqttBrokerNode.edit(); - mqttOutNode.mqttBrokerNode.setServer("localhost", moscaSettings.port); - mqttOutNode.mqttBrokerNode.clickOk(); - mqttOutNode.clickOk(); - - workspace.deploy(); - }); - - it('Connect to an MQTT broker', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("num", 22); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.setTopic("sensors/livingroom/temp"); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/livingroom/temp"); - mqttInNode.setQoS("2"); - mqttInNode.clickOk(); - - mqttInNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"22"'); - }); - - // skip this case since it is same as other cases. - it.skip('Publish messages to a topic'); - - it('Set the topic of a published message', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - injectNode.edit(); - injectNode.setPayload("num", 22); - injectNode.setTopic("sensors/kitchen/temperature"); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - // The code for confirmation starts from here. - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var debugNode = workspace.addNode("debug"); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/kitchen/temperature"); - mqttInNode.clickOk(); - - mqttInNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"22"'); - }); - - it('Publish a retained message to a topic', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - injectNode.edit(); - injectNode.setPayload("num", 22); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.setTopic("sensors/livingroom/temp"); - mqttOutNode.setRetain("true"); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - - // The code for confirmation starts from here. - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var debugNode = workspace.addNode("debug"); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/livingroom/temp"); - mqttInNode.clickOk(); - - mqttInNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(true); - debugTab.getMessage().should.eql('"22"'); - }); - - // skip this case since it is same as other cases. - it.skip('Subscribe to a topic'); - - it('Receive a parsed JSON message', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var jsonNode = workspace.addNode("json"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("json", '{"sensor_id": 1234, "temperature": 13 }'); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.setTopic("sensors/livingroom/temp"); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/#"); - mqttInNode.setQoS("2"); - mqttInNode.clickOk(); - - jsonNode.edit(); - jsonNode.setProperty("payload"); - jsonNode.clickOk(); - - mqttInNode.connect(jsonNode); - jsonNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql(['1234', '13']); - }); - }); -}); diff --git a/test/editor/wdio.conf.js b/test/editor/wdio.conf.js deleted file mode 100644 index 4e5a602e0..000000000 --- a/test/editor/wdio.conf.js +++ /dev/null @@ -1,338 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var browserstack = require('browserstack-local'); -exports.config = { - - // - // ================== - // Specify Test Files - // ================== - // Define which test specs should run. The pattern is relative to the directory - // from which `wdio` was called. Notice that, if you are calling `wdio` from an - // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working - // directory is where your package.json resides, so `wdio` will be called from there. - // - specs: [ - './test/editor/**/*_uispec.js' - ], - // Patterns to exclude. - exclude: [ - // 'path/to/excluded/files' - ], - // - // ============ - // Capabilities - // ============ - // Define your capabilities here. WebdriverIO can run multiple capabilities at the same - // time. Depending on the number of capabilities, WebdriverIO launches several test - // sessions. Within your capabilities you can overwrite the spec and exclude options in - // order to group specific specs to a specific capability. - // - // First, you can define how many instances should be started at the same time. Let's - // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have - // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec - // files and you set maxInstances to 10, all spec files will get tested at the same time - // and 30 processes will get spawned. The property handles how many capabilities - // from the same test should run tests. - // - // maxInstances: 10, - // - // If you have trouble getting all important capabilities together, check out the - // Sauce Labs platform configurator - a great tool to configure your capabilities: - // https://docs.saucelabs.com/reference/platforms-configurator - // - // capabilities: [{ - // maxInstances can get overwritten per capability. So if you have an in-house Selenium - // grid with only 5 firefox instances available you can make sure that not more than - // 5 instances get started at a time. - // maxInstances: 5, - // - // browserName: 'firefox' - // }], - // - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // - // By default WebdriverIO commands are executed in a synchronous way using - // the wdio-sync package. If you still want to run your tests in an async way - // e.g. using promises you can set the sync option to false. - sync: true, - // - // Level of logging verbosity: silent | verbose | command | data | result | error - logLevel: 'silent', - // - // Enables colors for log output. - coloredLogs: true, - // - // Warns when a deprecated command is used - deprecationWarnings: false, - // - // If you only want to run your tests until a specific amount of tests have failed use - // bail (default is 0 - don't bail, run all tests). - bail: 0, - // - // Saves a screenshot to a given path if a command fails. - screenshotPath: './test/errorShots/', - // - // Set a base URL in order to shorten url command calls. If your `url` parameter starts - // with `/`, the base url gets prepended, not including the path portion of your baseUrl. - // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url - // gets prepended directly. - baseUrl: 'http://localhost', - // - // Default timeout for all waitFor* commands. - waitforTimeout: 20000, - // - // Default timeout in milliseconds for request - // if Selenium Grid doesn't send response - connectionRetryTimeout: 90000, - // - // Default request retries count - connectionRetryCount: 3, - // - // Initialize the browser instance with a WebdriverIO plugin. The object should have the - // plugin name as key and the desired plugin options as properties. Make sure you have - // the plugin installed before running any tests. The following plugins are currently - // available: - // WebdriverCSS: https://github.com/webdriverio/webdrivercss - // WebdriverRTC: https://github.com/webdriverio/webdriverrtc - // Browserevent: https://github.com/webdriverio/browserevent - // plugins: { - // webdrivercss: { - // screenshotRoot: 'my-shots', - // failedComparisonsRoot: 'diffs', - // misMatchTolerance: 0.05, - // screenWidth: [320,480,640,1024] - // }, - // webdriverrtc: {}, - // browserevent: {} - // }, - // - // Test runner services - // Services take over a specific job you don't want to take care of. They enhance - // your test setup with almost no effort. Unlike plugins, they don't add new - // commands. Instead, they hook themselves up into the test process. - //services: ['chromedriver'], - // - // Framework you want to run your specs with. - // The following are supported: Mocha, Jasmine, and Cucumber - // see also: http://webdriver.io/guide/testrunner/frameworks.html - // - // Make sure you have the wdio adapter package for the specific framework installed - // before running any tests. - framework: 'mocha', - // - // Test reporter for stdout. - // The only one supported by default is 'dot' - // see also: http://webdriver.io/guide/reporters/dot.html - reporters: ['spec'], - - // - // Options to be passed to Mocha. - // See the full list at http://mochajs.org/ - mochaOpts: { - timeout: 1000000, - ui: 'bdd' - }, - // - // ===== - // Hooks - // ===== - // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance - // it and to build services around it. You can either apply a single function or an array of - // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got - // resolved to continue. - /** - * Gets executed once before all workers get launched. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - onPrepare: function (config, capabilities) { - if (process.env.BROWSERSTACK) { - return new Promise(function (resolve, reject) { - var options = { key: exports.config.key }; - var proxy = process.env.http_proxy || process.env.HTTP_PROXY; - if (proxy) { - var proxyConfigs = proxy.match(/^(https?):\/\/(([^:@\/]+):([^:@\/]+)@)?([^:@\/]+)(:([^:@\/]+))?\/?$/); - if (proxyConfigs) { - var protocol = proxyConfigs[1]; - var user = proxyConfigs[3]; - var pass = proxyConfigs[4]; - var host = proxyConfigs[5]; - var port = proxyConfigs[7]; - if (!port) { - if (protocol === 'http') { - port = 80; - } else if (protocol === 'https') { - port = 443; - } - } - if (host) { options.proxyHost = host; } - if (port) { options.proxyPort = port; } - if (user) { options.proxyUser = user; } - if (pass) { options.proxyPass = pass; } - } else { - reject('error in parsing the environment variable, http_proxy'); - } - } - exports.bs_local = new browserstack.Local(); - exports.bs_local.start(options, function (error) { - if (error) { - return reject(error); - } - resolve(); - }); - }); - } - }, - /** - * Gets executed just before initialising the webdriver session and test framework. It allows you - * to manipulate configurations depending on the capability or spec. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // beforeSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed before test execution begins. At this point you can access to all global - * variables like `browser`. It is the perfect place to define custom commands. - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // before: function (capabilities, specs) { - // }, - /** - * Runs before a WebdriverIO command gets executed. - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - */ - // beforeCommand: function (commandName, args) { - // }, - - /** - * Hook that gets executed before the suite starts - * @param {Object} suite suite details - */ - // beforeSuite: function (suite) { - // }, - /** - * Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. - * @param {Object} test test details - */ - // beforeTest: function (test) { - // }, - /** - * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling - * beforeEach in Mocha) - */ - // beforeHook: function () { - // }, - /** - * Hook that gets executed _after_ a hook within the suite ends (e.g. runs after calling - * afterEach in Mocha) - */ - // afterHook: function () { - // }, - /** - * Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) ends. - * @param {Object} test test details - */ - // afterTest: function (test) { - // }, - /** - * Hook that gets executed after the suite has ended - * @param {Object} suite suite details - */ - // afterSuite: function (suite) { - // }, - - /** - * Runs after a WebdriverIO command gets executed - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - * @param {Number} result 0 - command success, 1 - command error - * @param {Object} error error object if any - */ - // afterCommand: function (commandName, args, result, error) { - // }, - /** - * Gets executed after all tests are done. You still have access to all global variables from - * the test. - * @param {Number} result 0 - test pass, 1 - test fail - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // after: function (result, capabilities, specs) { - // }, - /** - * Gets executed right after terminating the webdriver session. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // afterSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed after all workers got shut down and the process is about to exit. - * @param {Object} exitCode 0 - success, 1 - fail - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - onComplete: function(exitCode, config, capabilities) { - if (process.env.BROWSERSTACK) { - exports.bs_local.stop(function () {}); - } - } -}; - -if (process.env.BROWSERSTACK) { - exports.config.maxInstances = 1; - if (process.env.BROWSERSTACK_USERNAME && process.env.BROWSERSTACK_ACCESS_KEY) { - exports.config.user = process.env.BROWSERSTACK_USERNAME; - exports.config.key = process.env.BROWSERSTACK_ACCESS_KEY; - } else { - console.log('You need to set the following environment variables.'); - console.log('BROWSERSTACK_USERNAME='); - console.log('BROWSERSTACK_ACCESS_KEY='); - } - exports.config.services = ['browserstack']; - var capabilities = []; - capabilities.push({ os: 'Windows', os_version: '10', browser: 'Chrome', resolution: '1920x1080', 'browserstack.local': true }); - capabilities.push({ os: 'Windows', os_version: '10', browser: 'Firefox', resolution: '1920x1080', 'browserstack.local': true }); - capabilities.push({ os: 'OS X', os_version: 'Catalina', browser: 'Chrome', resolution: '1920x1080', 'browserstack.local': true }); - capabilities.push({ os: 'OS X', os_version: 'Catalina', browser: 'Firefox', resolution: '1920x1080', 'browserstack.local': true }); - exports.config.capabilities = capabilities; -} else { - exports.config.maxInstances = 10; - exports.config.port = 9515; - exports.config.path = '/'; - exports.config.services = ['chromedriver']; - exports.config.capabilities = [{ - maxInstances: 2, - browserName: 'chrome', - 'goog:chromeOptions': { - args: process.env.NODE_RED_NON_HEADLESS - // Runs tests with opening a browser. - ? ['--disable-gpu', '--no-sandbox'] - // Runs tests without opening a browser. - : ['--headless', '--disable-gpu', 'window-size=1920,1080', '--no-sandbox'] - } - }]; -} diff --git a/test/node_modules/nr-test-utils/index.js b/test/node_modules/nr-test-utils/index.js deleted file mode 100644 index 56bc369e6..000000000 --- a/test/node_modules/nr-test-utils/index.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -const path = require("path"); -const fs = require("fs"); - -const PACKAGE_ROOT = "../../../packages/node_modules"; - -module.exports = { - require: function(file) { - // console.log(path.join(__dirname,PACKAGE_ROOT,file)) - return require(path.join(PACKAGE_ROOT,file)); - }, - resolve: function(file) { - return path.resolve(path.join(__dirname,PACKAGE_ROOT,file)); - } -} diff --git a/test/node_modules/nr-test-utils/package.json b/test/node_modules/nr-test-utils/package.json deleted file mode 100644 index ee32d2320..000000000 --- a/test/node_modules/nr-test-utils/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "nr-test-utils", - "version": "0.20.0", - "license": "Apache-2.0", - "private": true -} diff --git a/test/nodes/core/common/20-inject_spec.js b/test/nodes/core/common/20-inject_spec.js deleted file mode 100644 index f760bb646..000000000 --- a/test/nodes/core/common/20-inject_spec.js +++ /dev/null @@ -1,669 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var injectNode = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); - -describe('inject node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - helper.stopServer(done); - }); - }); - - function basicTest(type, val, rval) { - it('inject value ('+type+')', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: val, payloadType: type, wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - if (rval) { - msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); - } - else { - msg.should.have.property("payload", val); - } - done(); - } catch (err) { - done(err); - } - }); - n1.receive({}); - }); - }); - } - - basicTest("num", 10); - basicTest("str", "10"); - basicTest("bool", true); - var val_json = '{ "x":"vx", "y":"vy", "z":"vz" }'; - basicTest("json", val_json, JSON.parse(val_json)); - var val_buf = "[1,2,3,4,5]"; - basicTest("bin", val_buf, Buffer.from(JSON.parse(val_buf))); - - it('inject value of environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "NR_TEST", payloadType: "env", wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "foo"); - done(); - } catch (err) { - done(err); - } - }); - process.env.NR_TEST = 'foo'; - n1.receive({}); - }); - }); - - it('sets the value of flow context property', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "changeMe"); - done(); - } catch (err) { - done(err); - } - }); - n1.context().flow.set("flowValue", "changeMe"); - n1.receive({}); - }); - }); - - it('sets the value of persistable flow context property', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "#:(memory0)::flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "changeMe"); - done(); - } catch (err) { - done(err); - } - }); - n1.context().flow.set("flowValue", "changeMe", "memory0", function (err) { - n1.receive({}); - }); - }); - }); - }); - - it('sets the value of two persistable flow context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "flow", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var global = n0.context().flow; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - - it('sets the value of global context property', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "globalValue", payloadType: "global", wires: [["n2"]]}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "changeMe"); - done(); - } catch (err) { - done(err); - } - }); - n1.context().global.set("globalValue", "changeMe"); - n1.receive({}); - }); - }); - - it('sets the value of persistable global context property', function (done) { - var flow = [{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "foo"); - done(); - } catch (err) { - done(err); - } - }); - var global = n1.context().global; - global.set("val", "foo", "memory1", function (err) { - n1.receive({}); - }); - }); - }); - }); - - it('sets the value of two persistable global context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var global = n0.context().global; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - - it('sets the value of persistable flow & global context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var context = n0.context(); - var flow = context.flow; - var global = context.global; - flow.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - - it('sets the value of two persistable global context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var global = n0.context().global; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - it('should inject once with default delay property', function(done) { - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('onceDelay', 100); - done(); - }); - }); - - it('should inject once with default delay', function(done) { - var timestamp = new Date(); - timestamp.setSeconds(timestamp.getSeconds() + 1); - - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 't1'); - msg.should.have.property('payload'); - should(msg.payload).be.lessThan(timestamp.getTime()); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should inject once with 500 msec. delay', function(done) { - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, onceDelay: 0.5, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('onceDelay', 500); - done(); - }); - }); - - it('should inject once with delay of two seconds', function(done) { - this.timeout(2700); // have to wait for the inject with delay of two seconds - - var timestamp = new Date(); - timestamp.setSeconds(timestamp.getSeconds() + 1); - - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, onceDelay: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 't1'); - should(msg.payload).be.greaterThan(timestamp.getTime()); - done(); - }); - }); - }); - - it('should inject repeatedly', function(done) { - - helper.load(injectNode, [{id:"n1", type:"inject", - payload:"payload", topic: "t2", - repeat: 0.2, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property('topic', 't2'); - msg.should.have.property('payload', 'payload'); - count += 1; - if (count > 2) { - helper.clearFlows().then(function() { - done(); - }); - } - }); - }); - }); - - it('should inject once with delay of two seconds and repeatedly', function(done) { - var timestamp = new Date(); - timestamp.setSeconds(timestamp.getSeconds() + 1); - - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", repeat: 0.2, - once: true, onceDelay: 1.2, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property('topic', 't1'); - should(msg.payload).be.greaterThan(timestamp.getTime()); - count += 1; - if (count > 2) { - helper.clearFlows().then(function() { - done(); - }); - } - }); - }); - }); - - it('should inject with cron', function(done) { - helper.load(injectNode, [{id:"n1", type:"inject", - payloadType:"date", topic: "t3", - crontab: "* * * * * *", wires:[["n3"]] }, - {id:"n3", type:"helper"}], - function() { - var n3 = helper.getNode("n3"); - n3.on("input", function(msg) { - msg.should.have.property('topic', 't3'); - msg.should.have.property('payload').be.a.Number(); - helper.clearFlows().then(function() { - done(); - }); - }); - }); - }); - - - it('should inject multiple properties ', function (done) { - var flow = [{id: "n1", type: "inject", props: [{p:"topic", v:"t1", vt:"str"}, {p:"payload", v:"foo", vt:"str"}, {p:"x", v: 10, "vt":"num"}, {p:"y", v: "x+2", "vt":"jsonata"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "foo"); - msg.should.have.property("x", 10); - msg.should.have.property("y", 12); - done(); - } catch (err) { - done(err); - } - }); - n1.receive({}); - }); - }); - - - it('should inject custom properties in message', function (done) { - //n1: inject node with { topic:"static", payload:"static", bool1:true, str1:"1" } - var flow = [{id: "n1", type: "inject", props: [{p:"payload", v:"static", vt:"str"}, {p:"topic", v:"static", vt:"str"}, {p:"bool1", v:"true", vt:"bool"}, {p:"str1", v:"1", vt:"str"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.not.have.property("payload"); //payload removed - msg.should.have.property("topic", "t_override"); //changed value to t_override - msg.should.have.property("str1", 1);//changed type from str to num - msg.should.have.property("num1", 1);//new prop - msg.should.have.property("bool1", false);//changed value to false - done(); - } catch (err) { - done(err); - } - }); - n1.receive({ __user_inject_props__: [ - {p:"topic", v:"t_override", vt:"str"}, //change value to t_override - {p:"str1", v:"1", vt:"num"}, //change type - {p:"num1", v:"1", vt:"num"}, //new prop - {p:"bool1", v:"false", vt:"bool"}, //change value to false - ]}); - }); - }); - - - it('should inject multiple properties using legacy props if needed', function (done) { - var flow = [{id: "n1", type: "inject", payload:"123", payloadType:"num", topic:"foo", props: [{p:"topic", vt:"str"}, {p:"payload"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "foo"); - msg.should.have.property("payload", 123); - done(); - } catch (err) { - done(err); - } - }); - n1.receive({}); - }); - }); - - - it('should report invalid JSONata expression', function (done) { - var flow = [{id: "n1", type: "inject", props: [{p:"topic", v:"t1", vt:"str"}, {p:"payload", v:"@", vt:"jsonata"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.not.have.property("payload"); - count++; - if (count == 2) { - done(); - } - } catch (err) { - done(err); - } - }); - n1.on("call:error", function(err) { - count++; - if (count == 2) { - done(); - } - }); - n1.receive({}); - }); - }); - - describe('post', function() { - it('should inject message', function(done) { - helper.load(injectNode, - [{id:"n1", type:"inject", - payloadType:"str", topic: "t4",payload:"hello", - wires:[["n4"]] }, - { id:"n4", type:"helper"}], function() { - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - msg.should.have.property('topic', 't4'); - msg.should.have.property('payload', 'hello'); - helper.clearFlows().then(function() { - done(); - }); - }); - try { - helper.request() - .post('/inject/n1') - .expect(200).end(function(err) { - if (err) { - console.log(err); - return helper.clearFlows() - .then(function () { - done(err); - }); - } - }); - } catch(err) { - done(err); - } - }); - }); - - it('should inject custom properties in posted message', function(done) { - var flow = [{id:"n1", type:"inject", payloadType:"str", topic: "t4",payload:"hello", wires:[["n4"]] }, - { id:"n4", type:"helper"}]; - helper.load(injectNode, flow, function() { - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - msg.should.not.have.property("payload"); //payload removed - msg.should.have.property("topic", "t_override"); //changed value to t_override - msg.should.have.property("str1", "1"); //injected prop - msg.should.have.property("num1", 1); //injected prop - msg.should.have.property("bool1", true); //injected prop - - helper.clearFlows().then(function() { - done(); - }); - }); - try { - helper.request() - .post('/inject/n1') - .send({ __user_inject_props__: [ - {p:"topic", v:"t_override", vt:"str"}, //change value to t_override - {p:"str1", v:"1", vt:"str"}, //new prop - {p:"num1", v:"1", vt:"num"}, //new prop - {p:"bool1", v:"true", vt:"bool"}, //new prop - ]}) - .expect(200).end(function(err) { - if (err) { - console.log(err); - return helper.clearFlows() - .then(function () { - done(err); - }); - } - }); - } catch(err) { - done(err); - } - }); - }); - - it('should fail for invalid node', function(done) { - helper.request().post('/inject/invalid').expect(404).end(done); - }); - }); -}); diff --git a/test/nodes/core/common/21-debug_spec.js b/test/nodes/core/common/21-debug_spec.js deleted file mode 100644 index ae0d9a48e..000000000 --- a/test/nodes/core/common/21-debug_spec.js +++ /dev/null @@ -1,659 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var debugNode = require("nr-test-utils").require("@node-red/nodes/core/common/21-debug.js"); -var helper = require("node-red-node-test-helper"); -var WebSocket = require('ws'); - -describe('debug node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - beforeEach(function (done) { - setTimeout(function() { - done(); - }, 55); - }); - - afterEach(function() { - helper.unload(); - }); - - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"debug", name:"Debug", complete:"false" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'Debug'); - n1.should.have.property('complete', "payload"); - done(); - }); - }); - - it('should publish on input', function(done) { - var flow = [{id:"n1", type:"debug", name:"Debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",name:"Debug",msg:"test",path:"global", - format:"string[4]",property:"payload"} - }]); - }, done); - }); - }); - - it('should publish to console', function(done) { - var flow = [{id:"n1", type:"debug", console:"true"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - var count = 0; - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string[4]",path:"global"} - }]); - count++; - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO, id:'n1',type:'debug',msg:'test', timestamp:tstmp,path:"global"}); - - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish complete message', function(done) { - var flow = [{id:"n1", type:"debug", complete:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg:'{"payload":"test"}',format:"Object",path:"global"} - }]); - }, done); - }); - }); - - it('should publish complete message to console', function(done) { - var flow = [{id:"n1", type:"debug", complete:"true", console:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg:'{"payload":"test"}',format:"Object",path:"global"} - }]); - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO, id:"n1",type:"debug",msg:'\n{ payload: \'test\' }',timestamp:tstmp,path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish other property', function(done) { - var flow = [{id:"n1", type:"debug", complete:"foo" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test", foo:"bar"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string[3]",path:"global"} - }]); - }, done); - }); - }); - - it('should publish multi-level properties', function(done) { - var flow = [{id:"n1", type:"debug", complete:"foo.bar" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test", foo: {bar:"bar"}}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string[3]",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an Error', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: new Error("oops")}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'{"name":"Error","message":"oops"}',property:"payload",format:"error",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a boolean', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: true}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg: 'true',property:"payload",format:"boolean",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a number', function(done) { - var flow = [{id:"n1", type:"debug", console:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: 7}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"7",property:"payload",format:"number",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a NaN', function(done) { - var flow = [{id:"n1", type:"debug", console:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Number.NaN}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"NaN",property:"payload",format:"number",path:"global"} - }]); - }, done); - }); - }); - - it('should publish with no payload', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"undefined",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a null', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:null}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"null",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an object', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {type:'foo'}}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an array', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: [0,1,2,3]}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg: '[0,1,2,3]',format:"array[4]", - property:"payload",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an object with circular references', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - var o = { name: 'bar' }; - o.o = o; - n1.emit("input", {payload: o}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:'{"name":"bar","o":"[Circular ~]"}', - property:"payload",format:"Object",path:"global" - } - }]); - }, done); - }); - }); - - it('should publish an object to console', function(done) { - var flow = [{id:"n1", type:"debug", console:"true"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {type:'foo'}}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"} - }]); - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:'\n{ type: \'foo\' }',timestamp:tstmp,path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish a string after a newline to console if the string contains \\n', function(done) { - var flow = [{id:"n1", type:"debug", console:"true"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test\ntest"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"test\ntest",property:"payload",format:"string[9]",path:"global"} - }]); - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:"\ntest\ntest",timestamp:tstmp,path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish complete message with edit', function(done) { - var flow = [{id:"n1", type:"debug", name:"Debug", complete: "true", - targetType: "jsonata", complete: '"<" & payload & ">"'}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",name:"Debug",msg:"", - format:"string[6]",path:"global"} - }]); - }, done); - }); - }); - - it('should truncate a long message', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:Array(1002).join("X")}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg: Array(1001).join("X")+'...', - property:"payload", - format:"string[1001]", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a long string in the object', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {foo: Array(1002).join("X")}}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:'{"foo":"'+Array(1001).join("X")+'..."}', - property:"payload", - format:"Object", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a large array', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Array(1001).fill("X")}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:JSON.stringify({ - __enc__: true, - type: "array", - data: Array(1000).fill("X"), - length: 1001 - }), - property:"payload", - format:"array[1001]", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a large array in the object', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {foo: Array(1001).fill("X")}}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:JSON.stringify({ - foo:{ - __enc__: true, - type: "array", - data: Array(1000).fill("X"), - length: 1001 - } - }), - property:"payload", - format:"Object", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a large buffer', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Buffer.alloc(501,"\"")}); - }, function(msg) { - var a = JSON.parse(msg); - a[0].should.eql({ - topic:"debug", - data:{ - id:"n1", - msg: Array(1001).join("2"), - property:"payload", - format:"buffer[501]", - path:"global" - } - }); - }, done); - }); - }); - - it('should truncate a large buffer in the object', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {foo: Buffer.alloc(1001,"X")}}); - }, function(msg) { - var a = JSON.parse(msg); - a[0].should.eql({ - topic:"debug", - data:{ - id:"n1", - msg:JSON.stringify({ - foo:{ - type: "Buffer", - data: Array(1000).fill(88), - __enc__: true, - length: 1001 - } - }), - property:"payload", - format:"Object", - path:"global" - } - }); - }, done); - }); - }); - - it('should convert Buffer to hex', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Buffer.from('HELLO', 'utf8')}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:'48454c4c4f', - property:"payload", - format:"buffer[5]", - path:"global" - } - }]); - }, done); - }); - }); - - it('should publish when active', function(done) { - var flow = [{id:"n1", type:"debug", active: false }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"message 1"}); - helper.request() - .post('/debug/n1/enable') - .expect(200).end(function(err) { - if (err) { return done(err); } - n1.emit("input", {payload:"message 2"}); - }); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string[9]",path:"global"} - }]); - }, done); - }); - }); - - it('should not publish when inactive', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function(close) { - helper.request() - .post('/debug/n1/disable') - .expect(201).end(function(err) { - if (err) { - close(); - return done(err); - } - n1.emit("input", {payload:"message"}); - setTimeout(function() { - close(); - done(); - }, 200); - }); - }, function(msg) { - should.fail(null,null,"unexpected message"); - }, function() {}); - }); - }); - - describe('post', function() { - it('should return 404 on invalid state', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - helper.request() - .post('/debug/n1/foobar') - .expect(404).end(done); - }); - }); - - it('should return 404 on invalid node', function(done) { - helper.request() - .post('/debug/n99/enable') - .expect(404).end(done); - }); - - it('should return 400 for invalid bulk disable', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - helper.request() - .post('/debug/disable') - .send({}) - .set('Content-type', 'application/json') - .expect(400).end(done); - }); - - }) - - it('should return success for bulk disable', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - helper.request() - .post('/debug/disable') - .send({nodes:['n1']}) - .set('Content-type', 'application/json') - .expect(201).end(done); - }); - - }) - }); - - describe('get', function() { - it('should return the view.html', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - helper.request() - .get('/debug/view/view.html') - .expect(200) - .end(done); - }); - }); - }); - -}); - -function websocket_test(open_callback, message_callback, done_callback) { - var ws = new WebSocket(helper.url() + "/comms"); - var close_callback = function() { ws.close(); }; - ws.on('open', function() { open_callback(close_callback); }); - ws.on('message', function(msg) { - try { - message_callback(msg, close_callback); - ws.close(); - done_callback(); - } catch(err) { - done_callback(err); - } - }); -} diff --git a/test/nodes/core/common/25-catch_spec.js b/test/nodes/core/common/25-catch_spec.js deleted file mode 100644 index 466d912d7..000000000 --- a/test/nodes/core/common/25-catch_spec.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); -var helper = require("node-red-node-test-helper"); - -describe('catch Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should output a message when called', function(done) { - var flow = [ { id:"n1", type:"catch", name:"catch", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(catchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.should.have.property('name', 'catch'); - n2.on("input", function(msg) { - msg.should.be.a.Error(); - msg.toString().should.equal("Error: big error"); - done(); - }); - var err = new Error("big error"); - n1.emit("input", err); - }); - }); - -}); diff --git a/test/nodes/core/common/25-status_spec.js b/test/nodes/core/common/25-status_spec.js deleted file mode 100644 index 41b0a79c8..000000000 --- a/test/nodes/core/common/25-status_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-status.js"); -var helper = require("node-red-node-test-helper"); - -describe('status Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should output a message when called', function(done) { - var flow = [ { id:"n1", type:"status", name:"status", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(catchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.should.have.property('name', 'status'); - n2.on("input", function(msg) { - msg.text.should.equal("Oh dear"); - msg.should.have.property('source'); - msg.source.should.have.property('id',"12345"); - msg.source.should.have.property('type',"testnode"); - msg.source.should.have.property('name',"fred"); - done(); - }); - var mst = { - text: "Oh dear", - source: { - id: "12345", - type: "testnode", - name: "fred" - } - } - n1.emit("input", mst); - }); - }); - -}); diff --git a/test/nodes/core/common/60-link_spec.js b/test/nodes/core/common/60-link_spec.js deleted file mode 100644 index 5314eed15..000000000 --- a/test/nodes/core/common/60-link_spec.js +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var linkNode = require("nr-test-utils").require("@node-red/nodes/core/common/60-link.js"); -var helper = require("node-red-node-test-helper"); - -describe('link Node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded (link in)', function(done) { - var flow = [{id:"n1", type:"link in", name: "link-in" }]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'link-in'); - done(); - }); - }); - - it('should be loaded (link out)', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out" }]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'link-out'); - done(); - }); - }); - - it('should be linked', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2"]}, - {id:"n2", type:"link in", name: "link-in", wires:[["n3"]]}, - {id:"n3", type:"helper"}]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - var n3 = helper.getNode("n3"); - n3.on("input", function(msg) { - try { - msg.should.have.property('payload', 'hello'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - }); - }); - - it('should be linked to multiple nodes', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2", "n3"]}, - {id:"n2", type:"link in", name: "link-in0", wires:[["n4"]]}, - {id:"n3", type:"link in", name: "link-in1", wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - var n4 = helper.getNode("n4"); - var count = 0; - n4.on("input", function (msg) { - try { - msg.should.have.property('payload', 'hello'); - count++; - if(count == 2) { - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - }); - }); - - it('should be linked from multiple nodes', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out0", links:["n3"]}, - {id:"n2", type:"link out", name: "link-out1", links:["n3"]}, - {id:"n3", type:"link in", name: "link-in", wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n4 = helper.getNode("n4"); - var count = 0; - n4.on("input", function(msg) { - try { - msg.should.have.property('payload', 'hello'); - count++; - if(count == 2) { - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - n2.receive({payload:"hello"}); - }); - }); - - describe("link-call node", function() { - it('should call link-in node and get response', function(done) { - var flow = [{id:"link-in-1", type:"link in", wires: [[ "func"]]}, - {id:"func", type:"helper", wires: [["link-out-1"]]}, - {id:"link-out-1", type:"link out", mode: "return"}, - {id:"link-call", type:"link call", links:["link-in-1"], wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var func = helper.getNode("func"); - func.on("input", function(msg, send, done) { - msg.payload = "123"; - send(msg); - done(); - }) - var n1 = helper.getNode("link-call"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property('payload', '123'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - }); - }) - }); - - it('should allow nested link-call flows', function(done) { - var flow = [/** Multiply by 2 link flow **/ - {id:"li1", type:"link in", wires: [[ "m2"]]}, - {id:"m2", type:"helper", wires: [["lo1"]]}, - {id:"lo1", type:"link out", mode: "return"}, - /** Multiply by 3 link flow **/ - {id:"li2", type:"link in", wires: [[ "m3"]]}, - {id:"m3", type:"helper", wires: [["lo2"]]}, - {id:"lo2", type:"link out", mode: "return"}, - /** Multiply by 6 link flow **/ - {id:"li3", type:"link in", wires: [[ "link-call-1"]]}, - {id:"link-call-1", type:"link call", links:["m2"], wires:[["link-call-2"]]}, - {id:"link-call-2", type:"link call", links:["m3"], wires:[["lo3"]]}, - {id:"lo3", type:"link out", mode: "return"}, - /** Test Flow Entry **/ - {id:"link-call", type:"link call", links:["li3"], wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var m2 = helper.getNode("m2"); - m2.on("input", function(msg, send, done) { msg.payload *= 2 ; send(msg); done(); }) - var m3 = helper.getNode("m3"); - m3.on("input", function(msg, send, done) { msg.payload *= 3 ; send(msg); done(); }) - - var n1 = helper.getNode("link-call"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property('payload', 24); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:4}); - }); - }) -}); diff --git a/test/nodes/core/common/90-comment_spec.js b/test/nodes/core/common/90-comment_spec.js deleted file mode 100644 index a6f6a8284..000000000 --- a/test/nodes/core/common/90-comment_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var commentNode = require("nr-test-utils").require("@node-red/nodes/core/common/90-comment.js"); -var helper = require("node-red-node-test-helper"); - -describe('comment Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"comment", name: "comment" }]; - helper.load(commentNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'comment'); - done(); - }); - }); - -}); diff --git a/test/nodes/core/common/98-unknown_spec.js b/test/nodes/core/common/98-unknown_spec.js deleted file mode 100644 index 1b16a5c3b..000000000 --- a/test/nodes/core/common/98-unknown_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var unknown = require("nr-test-utils").require("@node-red/nodes/core/common/98-unknown.js"); -var helper = require("node-red-node-test-helper"); - -describe('unknown Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"unknown", name: "unknown" }]; - helper.load(unknown, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'unknown'); - done(); - }); - }); - -}); diff --git a/test/nodes/core/function/10-function_spec.js b/test/nodes/core/function/10-function_spec.js deleted file mode 100644 index 4f7f0b806..000000000 --- a/test/nodes/core/function/10-function_spec.js +++ /dev/null @@ -1,1721 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -var should = require("should"); -var functionNode = require("nr-test-utils").require("@node-red/nodes/core/function/10-function.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); -describe('function node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory1: { - module: "memory" - }, - memory2: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function() { - helper.unload().then(function () { - return Context.clean({allNodes:{}}); - }).then(function () { - return Context.close(); - }); - }); - - it('should send returned message using send()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should do something with the catch node', function(done) { - var flow = [{"id":"funcNode","type":"function","wires":[["goodNode"]],"func":"node.error('This is an error', msg);"},{"id":"goodNode","type":"helper"},{"id":"badNode","type":"helper"},{"id":"catchNode","type":"catch","scope":null,"uncaught":false,"wires":[["badNode"]]}]; - var catchNodeModule = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js") - helper.load([catchNodeModule, functionNode], flow, function() { - var funcNode = helper.getNode("funcNode"); - var catchNode = helper.getNode("catchNode"); - var goodNode = helper.getNode("goodNode"); - var badNode = helper.getNode("badNode"); - - badNode.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - msg.should.have.property('error'); - msg.error.should.have.property('message',"This is an error"); - msg.error.should.have.property('source'); - msg.error.source.should.have.property('id', "funcNode"); - done(); - }); - funcNode.receive({payload:"foo",topic: "bar"}); - }); - }); - - - - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"function", name: "function" }]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'function'); - done(); - }); - }); - - it('should send returned message', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should send returned message using send()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing node.id and node.name and node.outputCount', function(done) { - var flow = [{id:"n1",name:"test-function", outputs: 2, type:"function",wires:[["n2"]],func: "return [{ topic: node.name, payload:node.id, outputCount: node.outputCount }];"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - // Use this form of assert as `msg` is created inside - // the sandbox and doesn't get all the should.js monkey patching - should.equal(msg.payload, n1.id); - should.equal(msg.topic, n1.name); - should.equal(msg.outputCount, n1.outputs); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - function testSendCloning(args,done) { - var flow = [{id:"n1",type:"function",wires:[["n2"],["n2"]],func:"node.send("+args+"); msg.payload = 'changed';"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - } catch(err) { - done(err); - } - }); - var origMessage = {payload:"foo",topic: "bar"}; - n1.receive(origMessage); - }); - } - it('should clone single message sent using send()', function(done) { - testSendCloning("msg",done); - }); - it('should not clone single message sent using send(,false)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg,false); msg.payload = 'changed';"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'changed'); - done(); - }); - var origMessage = {payload:"foo",topic: "bar"}; - n1.receive(origMessage); - }); - }); - it('should clone first message sent using send() - array 1', function(done) { - testSendCloning("[msg]",done); - }); - it('should clone first message sent using send() - array 2', function(done) { - testSendCloning("[[msg],[null]]",done); - }); - it('should clone first message sent using send() - array 3', function(done) { - testSendCloning("[null,msg]",done); - }); - it('should clone first message sent using send() - array 3', function(done) { - testSendCloning("[null,[msg]]",done); - }); - - it('should pass through _topic', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - msg.should.have.property('_topic', 'baz'); - done(); - }); - n1.receive({payload:"foo",topic: "bar", _topic: "baz"}); - }); - }); - - it('should send to multiple outputs', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"],["n3"]], - func:"return [{payload: '1'},{payload: '2'}];"}, - {id:"n2", type:"helper"}, {id:"n3", type:"helper"} ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var count = 0; - n2.on("input", function(msg) { - should(msg).have.property('payload', '1'); - count++; - if (count == 2) { - done(); - } - }); - n3.on("input", function(msg) { - should(msg).have.property('payload', '2'); - count++; - if (count == 2) { - done(); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should send to multiple messages', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]], - func:"return [[{payload: 1},{payload: 2}]];"}, - {id:"n2", type:"helper"} ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - count++; - try { - should(msg).have.property('payload', count); - should(msg).have.property('_msgid', 1234); - if (count == 2) { - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", topic: "bar",_msgid:1234}); - }); - }); - - it('should allow input to be discarded by returning null', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return null"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function() { - done(); - }, 20); - n2.on("input", function(msg) { - should.fail(null,null,"unexpected message"); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle null amongst valid messages', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return [[msg,null,msg],null]"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n2MsgCount = 0; - var n3MsgCount = 0; - n2.on("input", function(msg) { - n2MsgCount++; - }); - n3.on("input", function(msg) { - n3MsgCount++; - }); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - n2MsgCount.should.equal(2); - n3MsgCount.should.equal(0); - done(); - },20); - }); - }); - - it('should get keys in global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.keys();return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - function testNonObjectMessage(functionText,done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:functionText}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n2MsgCount = 0; - n2.on("input", function(msg) { - n2MsgCount++; - }); - n1.receive({}); - setTimeout(function() { - try { - n2MsgCount.should.equal(0); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'function.error.non-message-returned'); - done(); - } catch(err) { - done(err); - } - },20); - }); - } - it('should drop and log non-object message types - string', function(done) { - testNonObjectMessage('return "foo"', done) - }); - it('should drop and log non-object message types - buffer', function(done) { - testNonObjectMessage('return Buffer.from("hello")', done) - }); - it('should drop and log non-object message types - array', function(done) { - testNonObjectMessage('return [[[1,2,3]]]', done) - }); - it('should drop and log non-object message types - boolean', function(done) { - testNonObjectMessage('return true', done) - }); - it('should drop and log non-object message types - number', function(done) { - testNonObjectMessage('return 123', done) - }); - - it('should handle and log script error', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"var a = 1;\nretunr"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'ReferenceError: retunr is not defined (line 2, col 1)'); - done(); - } catch(err) { - done(err); - } - },50); - }); - }); - - it('should handle node.on()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.on('close',function(){ node.log('closed')});"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - n1.close().then(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().INFO); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'closed'); - done(); - } catch(err) { - done(err); - } - }); - },100); - }); - }); - - it('should set node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count").should.equal("0"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should set persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1');context.set('count','1','memory2');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count", "memory2", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (single call, w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set(['count1','count2'],['0','1'],'memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count1", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count2", "memory1", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - - it('should set persistable node context (w callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1', function (err) { node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (w callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1', function (err) { context.set('count', '1', 'memory2', function (err) { node.send(msg); }); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count", "memory1", function (err, val2) { - val2.should.equal("0"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (single call, w callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set(['count1','count2'],['0','1'],'memory1', function(err) { node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count1", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count2", "memory1", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - - it('should set default persistable node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.get('count');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - function checkCallbackError(name, done) { - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', name); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'Error: Callback must be a function'); - done(); - } - catch (e) { - done(e); - } - },50); - } - - it('should get persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.get('count','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable node context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.get('count','memory1',function (err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys();return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get keys in persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys('memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in persistable node context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.keys('memory1', function(err, keys) { msg.payload=keys; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in default persistable node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys();return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n1.context().set("number","1","memory2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count").should.equal("0"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should set persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1');flow.set('count','1','memory2');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n2.context().flow.get("count", "memory2", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1', function (err) { node.send(msg); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1', function (err) { flow.set('count','1','memory2', function (err) { node.send(msg); }); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n2.context().flow.get("count", "memory2", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.get('count');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.get('count','memory1');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.get('count','memory1', function(err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.flow.get('count');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get keys in flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.keys();return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get keys in persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.keys('memory1');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.keys('memory1', function (err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().global.get("count").should.equal("0"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should set persistable global context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().global.get("count", "memory1", function(err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set persistable global context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0','memory1', function (err) { node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().global.get("count", "memory1", function(err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('count');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get persistable global context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('count', 'memory1');return msg;"}, - {id:"n2", type:"helper"}]; - initContext(function () { - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", 'memory1'); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable global context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.get('count', 'memory1', function (err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - initContext(function () { - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", 'memory1'); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.global.get('count');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get persistable global context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.global.get('count','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", "memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable global context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.global.get('count','memory1', function (err, val) { msg.payload = val; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", "memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should handle error on get persistable context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.get('count','memory1','callback');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n1.receive({payload:"foo",topic: "bar"}); - checkCallbackError('n1', done); - }); - }); - }); - - it('should handle error on set persistable context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.set('count','0','memory1','callback');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:"foo",topic: "bar"}); - checkCallbackError('n1', done); - }); - }); - }); - - it('should handle error on get keys in persistable context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.keys('memory1','callback');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n1.receive({payload:"foo",topic: "bar"}); - checkCallbackError('n1', done); - }); - }); - }); - - it('should handle setTimeout()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"setTimeout(function(){node.send(msg);},700);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var endTime = process.hrtime(startTime); - var nanoTime = endTime[0] * 1000000000 + endTime[1]; - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - if (600000000 < nanoTime && nanoTime < 800000000) { - done(); - } else { - try { - should.fail(null, null, "Delayed time was not between 900 and 1100 ms"); - } catch (err) { - done(err); - } - } - }); - var startTime = process.hrtime(); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle setInterval()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"setInterval(function(){node.send(msg);},100);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - count++; - if (count > 2) { - done(); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle clearInterval()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"var id=setInterval(null,100);setTimeout(function(){clearInterval(id);node.send(msg);},500);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing node.id', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = node.id; return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', n1.id); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing node.name', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = node.name; return msg;", "name":"name of node"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', n1.name); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should use the same Date object from outside the sandbox', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('typeTest')(new Date());return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("typeTest",function(d) { return d instanceof Date }); - n2.on("input", function(msg) { - msg.should.have.property('payload', true); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing env vars', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = env.get('_TEST_FOO_'); return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - delete process.env._TEST_FOO_; - - n2.on("input", function(msg) { - try { - if (count === 0) { - msg.should.have.property('payload', undefined); - process.env._TEST_FOO_ = "hello"; - count++; - n1.receive({payload:"foo",topic: "bar"}); - } else { - msg.should.have.property('payload', "hello"); - delete process.env._TEST_FOO_; - done(); - } - } catch(err) { - delete process.env._TEST_FOO_; - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should execute initialization', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = global.get('X'); return msg;",initialize:"global.set('X','bar');"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "bar"); - done(); - }); - n1.receive({payload: "foo"}); - }); - }); - - it('should wait completion of initialization', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = global.get('X'); return msg;",initialize:"global.set('X', '-'); return new Promise((resolve, reject) => setTimeout(() => { global.set('X','bar'); resolve(); }, 500));"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "bar"); - done(); - }); - n1.receive({payload: "foo"}); - }); - }); - - - - describe("finalize function", function() { - - it('should execute', function(done) { - var flow = [{id:"n1",type:"function",wires:[],func:"return msg;",finalize:"global.set('X','bar');"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var ctx = n1.context().global; - helper.unload().then(function () { - ctx.get('X').should.equal("bar"); - done(); - }); - }); - }); - - it('should allow accessing node.id and node.name and node.outputCount', function(done) { - var flow = [{id:"n1",name:"test-function", outputs: 2, type:"function",wires:[["n2"]],finalize:"global.set('finalize-data', { topic: node.name, payload:node.id, outputCount: node.outputCount});", func: "return msg;"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var ctx = n1.context().global; - helper.unload().then(function () { - const finalizeData = ctx.get('finalize-data'); - should.equal(finalizeData.payload, n1.id); - should.equal(finalizeData.topic, n1.name); - should.equal(finalizeData.outputCount, n1.outputs); - done(); - }); - }); - }); - - }) - - describe('externalModules', function() { - afterEach(function() { - delete RED.settings.functionExternalModules; - }) - it('should fail if using OS module with functionExternalModules set to false', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"os", module:"os"}]}, - {id:"n2", type:"helper"} - ]; - RED.settings.functionExternalModules = false; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - should.not.exist(n1); - done(); - }).catch(err => done(err)); - }) - - it('should fail if using OS module without it listed in libs', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;"}, - {id:"n2", type:"helper"} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var messageReceived = false; - n2.on("input", function(msg) { - messageReceived = true; - }); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - try { - messageReceived.should.be.false(); - done(); - } catch(err) { - done(err); - } - },20); - }).catch(err => done(err)); - }) - it('should require the OS module', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"os", module:"os"}]}, - {id:"n2", type:"helper"} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', require('os').type()); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }).catch(err => done(err)); - }) - it('should fail if module variable name clashes with sandbox builtin', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"flow", module:"os"}]}, - {id:"n2", type:"helper"} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - should.not.exist(n1); - done(); - }).catch(err => done(err)); - }) - }) - - - describe('Logger', function () { - - function testLog(initCode,funcCode,expectedLevel, done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: funcCode, initialize: initCode}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log()[expectedLevel]); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'test'); - done(); - } catch (err) { - done(err); - } - },10); - }); - } - - it('should log an Info Message', function (done) { - testLog("","node.log('test');","INFO",done); - }); - it('should log a Debug Message', function (done) { - testLog("","node.debug('test');","DEBUG",done); - }); - it('should log a Trace Message', function (done) { - testLog("","node.trace('test');","TRACE",done); - }); - it('should log a Warning Message', function (done) { - testLog("","node.warn('test');","WARN",done); - }); - it('should log an Error Message', function (done) { - testLog("","node.error('test');","ERROR",done); - }); - - it('should log an Info Message - initialise', function (done) { - testLog("node.log('test');","","INFO",done); - }); - it('should log a Debug Message - initialise', function (done) { - testLog("node.debug('test');","","DEBUG",done); - }); - it('should log a Trace Message - initialise', function (done) { - testLog("node.trace('test');","","TRACE",done); - }); - it('should log a Warning Message - initialise', function (done) { - testLog("node.warn('test');","","WARN",done); - }); - it('should log an Error Message - initialise', function (done) { - testLog("node.error('test');","","ERROR",done); - }); - - it('should catch thrown string', function (done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "throw \"small mistake\";"}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'small mistake'); - done(); - } catch (err) { - done(err); - } - },50); - }); - }); - it('should catch thrown number', function (done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "throw 99;"}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', '99'); - done(); - } catch (err) { - done(err); - } - },50); - }); - }); - it('should catch thrown object (bad practice)', function (done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "throw {a:1};"}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', '{"a":1}'); - done(); - } catch (err) { - done(err); - } - },50); - }); - }); - }); - - describe("init function", function() { - - it('should delay handling messages until init completes', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],initialize: ` - return new Promise((resolve,reject) => { - setTimeout(resolve,200) - })`, - func:"return msg;" - }, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var receivedMsgs = []; - n2.on("input", function(msg) { - msg.delta = Date.now() - msg.payload; - receivedMsgs.push(msg) - if (receivedMsgs.length === 5) { - var errors = receivedMsgs.filter(msg => msg.delta < 200) - if (errors.length > 0) { - done(new Error(`Message received before init completed - was ${msg.delta} expected >300`)) - } else { - done(); - } - } - }); - for (var i=0;i<5;i++) { - n1.receive({payload: Date.now(),topic: "msg"+i}); - } - }); - }); - - it('should allow accessing node.id and node.name and node.outputCount and sending message', function(done) { - var flow = [{id:"n1",name:"test-function", outputs: 1, type:"function",wires:[["n2"]],initialize:"setTimeout(function() { node.send({ topic: node.name, payload:node.id, outputCount: node.outputCount})},10)", func: ""}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - // Use this form of assert as `msg` is created inside - // the sandbox and doesn't get all the should.js monkey patching - should.equal(msg.payload, n1.id); - should.equal(msg.topic, n1.name); - should.equal(msg.outputCount, n1.outputs); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - }); -}); diff --git a/test/nodes/core/function/10-switch_spec.js b/test/nodes/core/function/10-switch_spec.js deleted file mode 100644 index 180ec9d36..000000000 --- a/test/nodes/core/function/10-switch_spec.js +++ /dev/null @@ -1,1153 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var switchNode = require("nr-test-utils").require("@node-red/nodes/core/function/10-switch.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context/"); - -describe('switch Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - RED.settings.nodeMessageBufferMaxLength = 0; - helper.stopServer(done); - }); - }); - - it('should be loaded with some defaults', function(done) { - var flow = [{"id":"switchNode1","type":"switch","name":"switchNode"}]; - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - switchNode1.should.have.property('name', 'switchNode'); - switchNode1.should.have.property('checkall', "true"); - switchNode1.should.have.property('rules', []); - done(); - }); - }); - - /** - * Test a switch node where one argument is consumed by the rule (such as greater than). - * @param rule - the switch rule (see 10-switc.js) string we're using - * @param ruleWith - whatever the rule should be executed with (say greater than 5) - * @param aCheckall - whether the switch flow should have the checkall flag set to true/false - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function genericSwitchTest(rule, ruleWith, aCheckall, shouldReceive, sendPayload, done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule,"v":ruleWith}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, shouldReceive, sendPayload, done); - } - - /** - * Test a switch node where NO arguments are consumed by the rule (such as TRUE/FALSE) - * @param rule - the switch rule (see 10-switc.js) string we're using - * @param aCheckall - whether the switch flow should have the checkall flag set to true/false - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function singularSwitchTest(rule, aCheckall, shouldReceive, sendPayload, done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, shouldReceive, sendPayload, done); - } - - /** - * Test a switch node where two arguments are consumed by the rule (such as between). - * @param rule - the switch rule (see 10-switc.js) string we're using - * @param ruleWith - whatever the rule should be executed with (say between 5...) - * @param ruleWith2 - whatever the rule should be executed with (say ...and 5) - * @param aCheckall - whether the switch flow should have the checkall flag set to true/false - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function twoFieldSwitchTest(rule, ruleWith, ruleWith2, aCheckall, shouldReceive, sendPayload, done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule,"v":ruleWith,"v2":ruleWith2}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, shouldReceive, sendPayload, done); - } - - /** - * Execute a switch test. Can specify whether the should node is expected to send a payload onwards to the helper node. - * The flow and the payload can be customised - * @param flow - the custom flow to be tested => must contain a switch node (switchNode1) wiring a helper node (helperNode1) - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function customFlowSwitchTest(flow, shouldReceive, sendPayload, done) { - customFlowMessageSwitchTest(flow,shouldReceive,{payload: sendPayload}, done); - } - - function customFlowMessageSwitchTest(flow, shouldReceive, message, done) { - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - if (shouldReceive === true) { - should.equal(msg,message); - done(); - } else { - should.fail(null, null, "We should never get an input!"); - } - } catch(err) { - done(err); - } - }); - switchNode1.receive(message); - if (shouldReceive === false) { - setTimeout(function() { - done(); - }, 200); - } - }); - } - - function customFlowSequenceSwitchTest(flow, seq_in, seq_out, repair, modifier, done) { - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var sid; - var count = 0; - if (modifier !== undefined) { - modifier(switchNode1); - } - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("payload", seq_out[count]); - msg.should.have.property("parts"); - var parts = msg.parts; - parts.should.have.property("id"); - var id = parts.id; - if (sid === undefined) { - sid = id; - } - else { - id.should.equal(sid); - } - if (repair) { - parts.should.have.property("index", count); - parts.should.have.property("count", seq_out.length); - } - else { - parts.should.have.property("index", msg.xindex); - parts.should.have.property("count", seq_in.length); - } - count++; - if (count === seq_out.length) { - done(); - } - } catch (e) { - done(e); - } - }); - var len = seq_in.length; - for (var i = 0; i < len; i++) { - var parts = {index:i, count:len, id:222}; - var msg = {payload:seq_in[i], xindex:i, parts:parts}; - switchNode1.receive(msg); - } - }); - } - - it('should check if payload equals given value', function(done) { - genericSwitchTest("eq", "Hello", true, true, "Hello", done); - }); - - it('should return nothing when the payload doesn\'t equal to desired string', function(done) { - genericSwitchTest("eq", "Hello", true, false, "Hello!", done); - }); - - it('should check if payload NOT equals given value', function(done) { - genericSwitchTest("neq", "Hello", true, true, "HEllO", done); - }); - - it('should return nothing when the payload does equal to desired string', function(done) { - genericSwitchTest("neq", "Hello", true, false, "Hello", done); - }); - - it('should check if payload equals given numeric value', function(done) { - genericSwitchTest("eq", 3, true, true, 3, done); - }); - - it('should return nothing when the payload doesn\'t equal to desired numeric value', function(done) { - genericSwitchTest("eq", 2, true, false, 4, done); - }); - - it('should check if payload NOT equals given numeric value', function(done) { - genericSwitchTest("neq", 55667744, true, true, -1234, done); - }); - - it('should return nothing when the payload does equal to desired numeric value', function(done) { - genericSwitchTest("neq", 10, true, false, 10, done); - }); - - it('should check if payload is less than given value', function(done) { - genericSwitchTest("lt", 3, true, true, 2, done); - }); - - it('should return nothing when the payload is not less than desired string', function(done) { - genericSwitchTest("lt", 3, true, false, 4, done); - }); - - it('should check if payload less than equals given value', function(done) { - genericSwitchTest("lte", 3, true, true, 3, done); - }); - - it('should check if payload is greater than given value', function(done) { - genericSwitchTest("gt", 3, true, true, 6, done); - }); - - it('should return nothing when the payload is not greater than desired string', function(done) { - genericSwitchTest("gt", 3, true, false, -1, done); - }); - - it('should check if payload is greater than/equals given value', function(done) { - genericSwitchTest("gte", 3, true, true, 3, done); - }); - - it('should return nothing when the payload is not greater than desired string', function(done) { - genericSwitchTest("gt", 3, true, false, -1, done); - }); - - it('should check if payload is greater than/equals given value', function(done) { - genericSwitchTest("gte", 3, true, true, 3, done); - }); - - it('should match if a payload has a required property', function(done) { - genericSwitchTest("hask", "a", true, true, {a:1}, done); - }); - it('should not match if a payload does not have a required property', function(done) { - genericSwitchTest("hask", "a", true, false, {b:1}, done); - }); - it('should not match if the key is not a string', function(done) { - genericSwitchTest("hask", 1, true, false, {a:1}, done); - }); - it('should not match if the parent object does not exist - null', function(done) { - genericSwitchTest("hask", "a", true, false, null, done); - }); - it('should not match if the parent object does not exist - undefined', function(done) { - genericSwitchTest("hask", "a", true, false, undefined, done); - }); - it('should check if payload is between given values', function(done) { - twoFieldSwitchTest("btwn", "3", "5", true, true, 4, done); - }); - - it('should check if payload is between given values in "wrong" order', function(done) { - twoFieldSwitchTest("btwn", "5", "3", true, true, 4, done); - }); - - it('should check if payload is between given string values', function(done) { - twoFieldSwitchTest("btwn", "c", "e", true, true, "d", done); - }); - - it('should check if payload is not between given values', function(done) { - twoFieldSwitchTest("btwn", 3, 5, true, false, 12, done); - }); - - it('should check if payload contains given value', function(done) { - genericSwitchTest("cont", "Hello", true, true, "Hello World!", done); - }); - - it('should return nothing when the payload doesn\'t contain desired string', function(done) { - genericSwitchTest("cont", "Hello", true, false, "This is not a greeting!", done); - }); - - it('should match regex', function(done) { - genericSwitchTest("regex", "[abc]+", true, true, "abbabac", done); - }); - - it('should check if payload if of type string ', function(done) { - genericSwitchTest("istype", "string", true, true, "Hello", done); - }); - it('should check if payload if of type number ', function(done) { - genericSwitchTest("istype", "number", true, true, 999, done); - }); - it('should check if payload if of type number 0', function(done) { - genericSwitchTest("istype", "number", true, true, 0, done); - }); - it('should check if payload if of type boolean true', function(done) { - genericSwitchTest("istype", "boolean", true, true, true, done); - }); - it('should check if payload if of type boolean false', function(done) { - genericSwitchTest("istype", "boolean", true, true, true, done); - }); - it('should check if payload if of type array ', function(done) { - genericSwitchTest("istype", "array", true, true, [1,2,3,"a","b"], done); - }); - it('should check if payload if of type buffer ', function(done) { - genericSwitchTest("istype", "buffer", true, true, Buffer.from("Hello"), done); - }); - it('should check if payload if of type object ', function(done) { - genericSwitchTest("istype", "object", true, true, {a:1,b:"b",c:true}, done); - }); - it('should check if payload if of type JSON string ', function(done) { - genericSwitchTest("istype", "json", true, true, JSON.stringify({a:1,b:"b",c:true}), done); - }); - it('should check if payload if of type JSON string (and fail if not) ', function(done) { - genericSwitchTest("istype", "json", true, false, "Hello", done); - }); - it('should check if payload if of type null', function(done) { - genericSwitchTest("istype", "null", true, true, null, done); - }); - it('should check if payload if of type undefined', function(done) { - genericSwitchTest("istype", "undefined", true, true, undefined, done); - }); - - it('should handle flow context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "foo", "propertyType": "flow", - "rules": [{"t": "eq", "v": "bar", "vt": "flow"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]], "z": "flow"}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("value"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().flow.set("foo", "flowValue"); - switchNode1.context().flow.set("bar", "flowValue"); - switchNode1.receive({payload: "value"}); - }); - }); - - it('should handle persistable flow context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "#:(memory1)::foo", "propertyType": "flow", - "rules": [{"t": "eq", "v": "#:(memory1)::bar", "vt": "flow"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]], "z": "flow"}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - initContext(function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("value"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().flow.set("foo", "flowValue", "memory1", function (err) { - switchNode1.context().flow.set("bar", "flowValue", "memory1", function (err) { - switchNode1.receive({payload: "value"}); - }); - }); - }); - }); - }); - - it('should handle global context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "foo", "propertyType": "global", - "rules": [{"t": "eq", "v": "bar", "vt": "global"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]]}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("value"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().global.set("foo", "globalValue"); - switchNode1.context().global.set("bar", "globalValue"); - switchNode1.receive({payload: "value"}); - }); - }); - - it('should handle persistable global context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "#:(memory1)::foo", "propertyType": "global", - "rules": [{"t": "eq", "v": "#:(memory1)::bar", "vt": "global"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]]}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - initContext(function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("foo"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().global.set("foo", "globalValue", "memory1", function (err) { - switchNode1.context().global.set("bar", "globalValue", "memory1", function (err) { - switchNode1.receive({payload: "foo"}); - }); - }); - }); - }); - }); - - it('should use a nested message property to compare value - matches', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"bar"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, true, {topic:"foo",payload:{"foo":"bar"}}, done); - }) - it('should use a nested message property to compare value - no match', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"bar"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, false, {topic:"foo",payload:{"foo":"none"}}, done); - - }) - - it('should use a nested message property to compare nested message property - matches', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"payload[msg.topic2]",vt:"msg"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, true, {topic:"foo",topic2:"foo2",payload:{"foo":"bar","foo2":"bar"}}, done); - }) - it('should use a nested message property to compare nested message property - no match', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"payload[msg.topic2]",vt:"msg"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, false, {topic:"foo",topic2:"foo2",payload:{"foo":"bar","foo2":"none"}}, done); - }) - - it('should match regex with ignore-case flag set true', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree","case":true}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, true, "oneTWOthree", done); - }); - it('should not match regex with ignore-case flag unset', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, false, "oneTWOthree", done); - }); - it('should not match regex with ignore-case flag set false', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree",case:false}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, false, "oneTWOthree", done); - }); - - it('should return nothing when the payload doesn\'t match regex', function(done) { - genericSwitchTest("regex", "\\d+", true, false, "This is not a digit", done); - }); - - it('should return nothing when the payload doesn\'t contain desired string', function(done) { - genericSwitchTest("cont", "Hello", true, false, "This is not a greeting!", done); - }); - - it('should check if input is true', function(done) { - singularSwitchTest(true, true, true, true, done); - }); - - it('sends nothing when input is false and checking for true', function(done) { - singularSwitchTest(true, true, false, false, done); - }); - - it('should check if input is indeed false', function(done) { - singularSwitchTest(false, true, true, false, done); - }); - - it('sends nothing when input is false and checking for true', function(done) { - singularSwitchTest(false, true, false, true, done); - }); - - it('should check if payload is empty (string)', function(done) { - singularSwitchTest("empty", true, true, "", done); - }); - it('should check if payload is empty (array)', function(done) { - singularSwitchTest("empty", true, true, [], done); - }); - it('should check if payload is empty (buffer)', function(done) { - singularSwitchTest("empty", true, true, Buffer.alloc(0), done); - }); - it('should check if payload is empty (object)', function(done) { - singularSwitchTest("empty", true, true, {}, done); - }); - it('should check if payload is empty (non-empty string)', function(done) { - singularSwitchTest("empty", true, false, "1", done); - }); - it('should check if payload is empty (non-empty array)', function(done) { - singularSwitchTest("empty", true, false, [1], done); - }); - it('should check if payload is empty (non-empty buffer)', function(done) { - singularSwitchTest("empty", true, false, Buffer.alloc(1), done); - }); - it('should check if payload is empty (non-empty object)', function(done) { - singularSwitchTest("empty", true, false, {a:1}, done); - }); - it('should check if payload is empty (null)', function(done) { - singularSwitchTest("empty", true, false, null, done); - }); - it('should check if payload is empty (undefined)', function(done) { - singularSwitchTest("empty", true, false, undefined, done); - }); - it('should check if payload is empty (0)', function(done) { - singularSwitchTest("empty", true, false, 0, done); - }); - - it('should check if payload is not empty (string)', function(done) { - singularSwitchTest("nempty", true, !true, "", done); - }); - it('should check if payload is not empty (array)', function(done) { - singularSwitchTest("nempty", true, !true, [], done); - }); - it('should check if payload is not empty (buffer)', function(done) { - singularSwitchTest("nempty", true, !true, Buffer.alloc(0), done); - }); - it('should check if payload is not empty (object)', function(done) { - singularSwitchTest("nempty", true, !true, {}, done); - }); - it('should check if payload is not empty (non-empty string)', function(done) { - singularSwitchTest("nempty", true, !false, "1", done); - }); - it('should check if payload is not empty (non-empty array)', function(done) { - singularSwitchTest("nempty", true, !false, [1], done); - }); - it('should check if payload is not empty (non-empty buffer)', function(done) { - singularSwitchTest("nempty", true, !false, Buffer.alloc(1), done); - }); - it('should check if payload is not empty (non-empty object)', function(done) { - singularSwitchTest("nempty", true, !false, {a:1}, done); - }); - it('should check if payload is not empty (null)', function(done) { - singularSwitchTest("nempty", true, false, null, done); - }); - it('should check if payload is not empty (undefined)', function(done) { - singularSwitchTest("nempty", true, false, undefined, done); - }); - it('should check if payload is not empty (0)', function(done) { - singularSwitchTest("nempty", true, false, 0, done); - }); - - it('should check input against a previous value', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{ "t": "gt", "v": "", "vt": "prev" }],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var c = 0; - helperNode1.on("input", function(msg) { - if (msg.payload) { - try { - if (c === 0) { - msg.payload.should.equal(1); - } - if (c === 1) { - msg.payload.should.equal(2); - done(); - } - c += 1; - } catch (err) { - done(err); - } - } else { - done(); - } - }); - switchNode1.receive({payload:1}); - switchNode1.receive({payload:0}); - switchNode1.receive({payload:-2}); - switchNode1.receive({payload:2}); - }); - }); - - it('should check input against a previous value (2nd option)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t": "btwn", "v": "10", "vt": "num", "v2": "", "v2t": "prev" }],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var c = 0; - helperNode1.on("input", function(msg) { - if (msg.payload) { - try { - if (c === 0) { - msg.payload.should.equal(20); - } - if (c === 1) { - msg.payload.should.equal(25); - done(); - } - c += 1; - } catch (err) { - done(err); - } - } else { - //done(); - } - }); - switchNode1.receive({payload:0}); - switchNode1.receive({payload:20}); // between 10 and 0 - switchNode1.receive({payload:30}); // between 10 and 20 - switchNode1.receive({payload:20}); // between 10 and 30 yes - switchNode1.receive({payload:30}); // between 10 and 20 no - switchNode1.receive({payload:25}); // between 10 and 30 yes - }); - }); - - it('should check if input is indeed null', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"null"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - if (msg.payload === null) { - done(); - } else { - console.log("msg is ",msg); - } - }); - switchNode1.receive({payload:null}); - }); - }); - - it('should check if input is indeed undefined', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"null"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - if (msg.payload === undefined) { - done(); - } - else { - console.log("msg is ",msg); - } - }); - switchNode1.receive({payload:undefined}); - }); - }); - it('should treat non-existant msg property conditional as undefined', function(done) { - var flow = [{"id":"switchNode1","type":"switch","z":"feee1df.c3263e","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"this.does.not.exist","vt":"msg"}],"checkall":"true","outputs":1,"x":190,"y":440,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var received = []; - helperNode1.on("input", function(msg) { - received.push(msg); - }); - // First message should be dropped as payload is not undefined - switchNode1.receive({topic:"messageOne",payload:""}); - // Second message should pass through as payload is undefined - switchNode1.receive({topic:"messageTwo",payload:undefined}); - setTimeout(function() { - try { - received.should.have.lengthOf(1); - received[0].should.have.a.property("topic","messageTwo"); - done(); - } catch(err) { - done(err); - } - },500) - }); - }); - - it('should check if input is indeed not null', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"nnull"}],checkall:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - if (msg.payload) { - done(); - } else { - try { - msg.payload.should.equal("Anything here"); - } catch (err) { - done(err); - } - } - }); - switchNode1.receive({payload:"Anything here"}); - }); - }); - - it('sends a message when the "else/otherwise" statement is selected' , function(done) { - singularSwitchTest("else", true, true, 123456, done); - }); - - it('handles more than one switch statement' , function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"eq","v":"Hello"},{"t":"cont","v":"ello"}, {"t":"else"}],checkall:true,outputs:3,wires:[["helperNode1"], ["helperNode2"], ["helperNode3"]]}, - {id:"helperNode1", type:"helper", wires:[]}, - {id:"helperNode2", type:"helper", wires:[]}, - {id:"helperNode3", type:"helper", wires:[]}]; - - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var helperNode2 = helper.getNode("helperNode2"); - var helperNode3 = helper.getNode("helperNode3"); - - var nodeHitCount = 0; - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello"); - nodeHitCount++; - } catch (err) { - done(err); - } - }); - helperNode2.on("input", function(msg) { - try { - msg.payload.should.equal("Hello"); - nodeHitCount++; - if (nodeHitCount == 2) { - done(); - } else { - try { - should.fail(null, null, "Both statements should be triggered!"); - } catch (err) { - done(err); - } - } - } catch (err) { - done(err); - } - }); - helperNode3.on("input", function(msg) { - try { - should.fail(null, null, "The otherwise/else statement should not be triggered here!"); - } catch (err) { - done(err); - } - }); - switchNode1.receive({payload:"Hello"}); - }); - }); - - it('stops after first statement' , function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"eq","v":"Hello"},{"t":"cont","v":"ello"}, {"t":"else"}],checkall:"false",outputs:3,wires:[["helperNode1"], ["helperNode2"], ["helperNode3"]]}, - {id:"helperNode1", type:"helper", wires:[]}, - {id:"helperNode2", type:"helper", wires:[]}, - {id:"helperNode3", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var helperNode2 = helper.getNode("helperNode2"); - var helperNode3 = helper.getNode("helperNode3"); - - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello"); - done(); - } catch (err) { - done(err); - } - }); - helperNode2.on("input", function(msg) { - try { - should.fail(null, null, "The otherwise/else statement should not be triggered here!"); - } catch (err) { - done(err); - } - }); - helperNode3.on("input", function(msg) { - try { - should.fail(null, null, "The otherwise/else statement should not be triggered here!"); - } catch (err) { - done(err); - } - }); - switchNode1.receive({payload:"Hello"}); - }); - }); - - it('should handle JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs(payload)",propertyType:"jsonata",rules:[{"t":"btwn","v":"$sqrt(16)","vt":"jsonata","v2":"$sqrt(36)","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, true, -5, done); - }); - - it('should handle flow and global contexts with JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs($flowContext(\"payload\"))",propertyType:"jsonata",rules:[{"t":"btwn","v":"$flowContext(\"vt\")","vt":"jsonata","v2":"$globalContext(\"v2t\")","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]],z:"flow"}, - {id:"helperNode1", type:"helper", wires:[],z:"flow"}, - {id:"flow",type:"tab"}]; - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - switchNode1.context().flow.set("payload",-5); - switchNode1.context().flow.set("vt",4); - switchNode1.context().global.set("v2t",6); - helperNode1.on("input", function(msg) { - try { - should.equal(msg.payload,"pass"); - done(); - } catch(err) { - done(err); - } - }); - switchNode1.receive({payload:"pass"}); - }); - }); - - it('should handle persistable flow and global contexts with JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs($flowContext(\"payload\",\"memory1\"))",propertyType:"jsonata",rules:[{"t":"btwn","v":"$flowContext(\"vt\",\"memory1\")","vt":"jsonata","v2":"$globalContext(\"v2t\",\"memory1\")","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]],z:"flow"}, - {id:"helperNode1", type:"helper", wires:[],z:"flow"}, - {id:"flow",type:"tab"}]; - helper.load(switchNode, flow, function() { - initContext(function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - switchNode1.context().flow.set(["payload","vt"],[-7,6],"memory1",function(){ - switchNode1.context().global.set("v2t",8,"memory1",function(){ - helperNode1.on("input", function(msg) { - try { - should.equal(msg.payload,"pass"); - done(); - } catch(err) { - done(err); - } - }); - switchNode1.receive({payload:"pass"}); - }); - }); - }); - }); - }); - - it('should handle env var expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"VAR",propertyType:"env",rules:[{"t":"eq","v":"VAL"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - process.env.VAR = "VAL"; - customFlowSwitchTest(flow, true, "OK", done); - }); - - - it('should take head of message sequence (no repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":3}],checkall:false,repair:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], false, undefined, done); - }); - - it('should take head of message sequence (repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":3}],checkall:false,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], true, undefined, done); - }); - - it('should take head of message sequence (w. context)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":"count",vt:"global"}],checkall:false,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], true, - function(node) { - node.context().global.set("count", 3); - }, done); - }); - - it('should take head of message sequence (w. JSONata)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":"1+4/2",vt:"jsonata"}],checkall:false,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], true, undefined, done); - }); - - it('should take tail of message sequence (no repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"tail","v":3}],checkall:true,repair:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [2, 3, 4], false, undefined, done); - }); - - it('should take tail of message sequence (repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"tail","v":3}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [2, 3, 4], true, undefined, done); - }); - - it('should take slice of message sequence (no repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"index","v":1,"v2":3}],checkall:true,repair:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [1, 2, 3], false, undefined, done); - }); - - it('should take slice of message sequence (repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"index","v":1,"v2":3}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [1, 2, 3], true, undefined, done); - }); - - it('should check JSONata expression is true', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"jsonata_exp","v":"payload%2 = 1","vt":"jsonata"}], - checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, true, 9, done); - }); - - it('should be able to use $I in JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"jsonata_exp","v":"$I % 2 = 1",vt:"jsonata"}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [1, 3], true, undefined, done); - }); - - it('should be able to use $N in JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"jsonata_exp","v":"payload >= $N-2",vt:"jsonata"}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [3, 4], true, undefined, done); - }); - - - function customFlowSequenceMultiSwitchTest(flow, seq_in, outs, repair, done) { - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("n1"); - var port_count = Object.keys(outs).length; - var sid; - var ids = new Array(port_count).fill(undefined); - var counts = new Array(port_count).fill(0); - var vals = new Array(port_count); - var recv_count = 0; - for (var id in outs) { - if (outs.hasOwnProperty(id)) { - var out = outs[id]; - vals[out.port] = out.vals; - recv_count += out.vals.length; - } - } - var count = 0; - function check_msg(msg, ix, vf) { - try { - msg.should.have.property("payload"); - var payload = msg.payload; - msg.should.have.property("parts"); - vf(payload).should.be.ok(); - var parts = msg.parts; - var evals = vals[ix]; - parts.should.have.property("id"); - var id = parts.id; - if (repair) { - if (ids[ix] === undefined) { - ids[ix] = id; - } - else { - ids[ix].should.equal(id); - } - parts.should.have.property("count", evals.length); - parts.should.have.property("index", counts[ix]); - } - else { - if (sid === undefined) { - sid = id; - } - else { - sid.should.equal(id); - } - parts.should.have.property("count", seq_in.length); - parts.should.have.property("index", msg.xindex); - } - var index = parts.index; - var eindex = counts[ix]; - var value = evals[eindex]; - payload.should.equal(value); - counts[ix]++; - count++; - if (count === recv_count) { - done(); - } - } - catch (e) { - done(e); - } - } - for (var id in outs) { - if (outs.hasOwnProperty(id)) { - (function() { - var node = helper.getNode(id); - var port = outs[id].port; - var vf = outs[id].vf; - node.on("input", function(msg) { - check_msg(msg, port, vf); - }); - })(); - } - } - for(var i in seq_in) { - if (seq_in.hasOwnProperty(i)) { - n1.receive({payload:seq_in[i], xindex:i, - parts:{index:i, count:seq_in.length, id:222}}); - } - } - }); - } - - it('should not repair message sequence for each port', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"gt","v":0},{"t":"lt","v":0},{"t":"else"}], - checkall:true,repair:false, - outputs:3,wires:[["n2"],["n3"],["n4"]]}, - {id:"n2", type:"helper", wires:[]}, - {id:"n3", type:"helper", wires:[]}, - {id:"n4", type:"helper", wires:[]} - ]; - var data = [ 1, -2, 2, 0, -1 ]; - var outs = { - "n2" : { port:0, vals:[1, 2], - vf:function(x) { return(x > 0); } }, - "n3" : { port:1, vals:[-2, -1], - vf:function(x) { return(x < 0); } }, - "n4" : { port:2, vals:[0], - vf:function(x) { return(x == 0); } }, - }; - customFlowSequenceMultiSwitchTest(flow, data, outs, false, done); - }); - - it('should repair message sequence for each port', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"gt","v":0},{"t":"lt","v":0},{"t":"else"}], - checkall:true,repair:true, - outputs:3,wires:[["n2"],["n3"],["n4"]]}, - {id:"n2", type:"helper", wires:[]}, // >0 - {id:"n3", type:"helper", wires:[]}, // <0 - {id:"n4", type:"helper", wires:[]} // ==0 - ]; - var data = [ 1, -2, 2, 0, -1 ]; - var outs = { - "n2" : { port:0, vals:[1, 2], - vf:function(x) { return(x > 0); } }, - "n3" : { port:1, vals:[-2, -1], - vf:function(x) { return(x < 0); } }, - "n4" : { port:2, vals:[0], - vf:function(x) { return(x == 0); } }, - }; - customFlowSequenceMultiSwitchTest(flow, data, outs, true, done); - }); - - it('should repair message sequence for each port (overlap)', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"gte","v":0},{"t":"lte","v":0},{"t":"else"}], - checkall:true,repair:true, - outputs:3,wires:[["n2"],["n3"],["n4"]]}, - {id:"n2", type:"helper", wires:[]}, // >=0 - {id:"n3", type:"helper", wires:[]}, // <=0 - {id:"n4", type:"helper", wires:[]} // none - ]; - var data = [ 1, -2, 2, 0, -1 ]; - var outs = { - "n2" : { port:0, vals:[1, 2, 0], - vf:function(x) { return(x >= 0); } }, - "n3" : { port:1, vals:[-2, 0, -1], - vf:function(x) { return(x <= 0); } }, - "n4" : { port:2, vals:[], - vf:function(x) { return(false); } }, - }; - customFlowSequenceMultiSwitchTest(flow, data, outs, true, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"tail","v":2}], - checkall:true,repair:false, - outputs:3,wires:[["n2"]]}, - {id:"n2", type:"helper", wires:[]} - ]; - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("n1"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "switch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "switch"); - evt.should.have.property('msg', "switch.errors.too-many"); - done(); - }, 150); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should handle invalid jsonata expression', function(done) { - - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$invalidExpression(payload)",propertyType:"jsonata",rules:[{"t":"btwn","v":"$sqrt(16)","vt":"jsonata","v2":"$sqrt(36)","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("switchNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "switch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "switchNode1"); - evt.should.have.property('type', "switch"); - done(); - }, 150); - n1.receive({payload:1}); - }); - }); - - - it('should handle empty rule', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[],checkall:true,outputs:0,wires:[]}]; - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("switchNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "switch"; - }); - if (logEvents.length === 0) { - done(); - } - }, 150); - n1.receive({payload:1}); - }); - }); -}); diff --git a/test/nodes/core/function/15-change_spec.js b/test/nodes/core/function/15-change_spec.js deleted file mode 100644 index b8d7be03b..000000000 --- a/test/nodes/core/function/15-change_spec.js +++ /dev/null @@ -1,1911 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var changeNode = require("nr-test-utils").require("@node-red/nodes/core/function/15-change.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); - -describe('change Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - helper.stopServer(done); - }); - }); - - it('should load node with defaults', function(done) { - var flow = [{ id: "c1", type: "change", name:"change1" }]; - helper.load(changeNode, flow, function() { - helper.getNode("c1").should.have.property("name", "change1"); - helper.getNode("c1").should.have.property("rules", [{fromt:'str',pt:'msg',tot:'str',t:undefined,p:''}]); - done(); - }); - }); - it('should load defaults if set to replace', function(done) { - var flow = [{ id: "c1", type: "change", name:"change1", action:"replace" }]; - helper.load(changeNode, flow, function() { - helper.getNode("c1").should.have.property("name", "change1"); - helper.getNode("c1").should.have.property("rules", [ {fromt: 'str', p: '', pt: 'msg', t: 'set', to: '', tot: 'str'} ]); - done(); - }); - }); - it('should load defaults if set to change', function(done) { - var flow = [{ id: "c1", type: "change", name:"change1", action:"change" }]; - helper.load(changeNode, flow, function() { - //console.log(helper.getNode("c1")); - helper.getNode("c1").should.have.property("name", "change1"); - helper.getNode("c1").should.have.property("rules", [ { from: '', fromRE:/(?:)/g,fromt: 'str', p: '',pt: 'msg', re: undefined, t: 'change', to: '',tot: 'str' } ]); - done(); - }); - }); - it('should no-op if there are no rules', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[],"action":"","property":"","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.eql(sentMsg); - done(); - } catch(err) { - done(err); - } - }); - var sentMsg = {payload:"leaveMeAlong"}; - changeNode1.receive(sentMsg); - }); - }); - - describe('#set' , function() { - - it('sets the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"changed","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("changed"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"changeMe"}); - }); - }); - - it('sets the value of global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t":"set","p":"globalValue","pt":"global","to":"changed","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.get("globalValue").should.equal("changed"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","changeMe"); - changeNode1.receive({payload:""}); - }); - }); - - it('sets the value of persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t":"set","p":"#:(memory1)::globalValue","pt":"global","to":"changed","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.get("globalValue", "memory1", function (err, val) { - val.should.equal("changed"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","changeMe","memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('sets the value and type of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "set", "p": "payload", "pt": "msg", "to": "12345", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(12345); - var t = typeof(msg.payload); - t.should.equal("number"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"changeMe"}); - }); - }); - - it('sets the value of an already set multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"bar","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({foo:{bar:"foo"}}); - }); - }); - - it('sets the value of an empty multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"bar","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({}); - }); - }); - - it('sets the value of a message property to another message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo","from":"","to":"msg.fred","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var rule = helper.getNode("changeNode1").rules[0]; - rule.t.should.eql('set'); - rule.tot.should.eql('msg'); - helperNode1.on("input", function(msg) { - try { - msg.foo.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({fred:"bar"}); - }); - }); - - it('sets the value of a multi-level message property to another multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"msg.fred.red","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({fred:{red:"bar"}}); - }); - }); - - it('doesn\'t set the value of a message property when the \'to\' message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"msg.fred.red","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - should.not.exist(msg.foo); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({}); - }); - }); - - it('overrides the value of a message property when the \'to\' message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.foo","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - should.not.exist(msg.payload); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello"}); - }); - }); - - it('sets the message property to null when the \'to\' message property equals null', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.foo","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - (msg.payload === null).should.be.true(); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello", foo:null}); - }); - }); - - it('does not set other properties using = inside to property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.otherProp=10","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - should.not.exist(msg.payload); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"changeMe"}); - }); - }); - - it('splits dot delimited properties into objects', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"pay.load","from":"","to":"10","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.pay.load.should.equal("10"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({pay:{load:"changeMe"}}); - }); - }); - - it('changes the value to flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"flowValue","tot":"flow"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("flowValue","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to persistable flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"#:(memory1)::flowValue","tot":"flow"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("flowValue","Hello World!","memory1",function(err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value to global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"globalValue","tot":"global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"#:(memory1)::globalValue","tot":"global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!","memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value to a number', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"123","tot":"num"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(123); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to a boolean value', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"true","tot":"bool"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(true); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to a js object', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":'{"a":123}',"tot":"json"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql({a:123}); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to a buffer object', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"[72,101,108,108,111,32,87,111,114,108,100]","tot":"bin"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var buff = Buffer.from("Hello World"); - msg.payload.should.eql(buff); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('sets the value of the message property to the current timestamp', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"set","p":"ts","pt":"msg","to":"","tot":"date"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - (Date.now() - msg.ts).should.be.approximately(0,50); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:Date.now()}); - }); - }); - - describe('env var', function() { - before(function() { - process.env.NR_TEST_A = 'foo'; - }) - after(function() { - delete process.env.NR_TEST_A; - }) - it('sets the value using env property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("foo"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - it('sets the value using env property from tab', function(done) { - var flow = [ - {"id":"tab1","type":"tab","env":[ - {"name":"NR_TEST_A", "value":"bar", "type": "str"} - ]}, - {"id":"changeNode1","type":"change","z":"tab1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]} - ]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - it('sets the value using env property from group', function(done) { - var flow = [ - {"id":"group1","type":"group","env":[ - {"name":"NR_TEST_A", "value":"bar", "type": "str"} - ]}, - {"id":"changeNode1","type":"change","g":"group1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]} - ]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - it('sets the value using env property from nested group', function(done) { - var flow = [ - {"id":"group1","type":"group","env":[ - {"name":"NR_TEST_A", "value":"bar", "type": "str"} - ]}, - {"id":"group2","type":"group","g":"group1","env":[]}, - {"id":"changeNode1","type":"change","g":"group2",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]} - ]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - }); - - it('changes the value using jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$length(payload)","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(12); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('reports invalid jsonata expression', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$invalid(payload)","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - done("Invalid jsonata expression passed message through"); - }); - changeNode1.on("call:error", function(err) { - // Expect error to be called - done(); - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value using flow context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$flowContext(\"foo\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - changeNode1.context().flow.set("foo","bar"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value using global context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$globalContext(\"foo\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - changeNode1.context().global.set("foo","bar"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value using persistable flow context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$flowContext(\"foo\",\"memory1\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("foo","bar","memory1",function(err){ - if(err){ - done(err); - }else{ - changeNode1.context().flow.set("foo","error!"); - changeNode1.receive({payload:"Hello World!"}); - } - }); - }); - }); - }); - - it('changes the value using persistable global context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$globalContext(\"foo\",\"memory1\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("foo","bar","memory1",function(err){ - if(err){ - done(err); - }else{ - changeNode1.context().global.set("foo","error!"); - changeNode1.receive({payload:"Hello World!"}); - } - }); - }); - }); - }); - - it('sets the value of a message property using a nested property', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(2); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"",lookup:{a:1,b:2},topic:"b"}); - }); - }); - - it('sets the value of a nested message property using a message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"lookup[msg.topic]","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.lookup.b.should.equal("newValue"); - done(); - } catch(err) { - done(err); - } - }); - var msg = { - payload: "newValue", - lookup:{a:1,b:2}, - topic:"b" - } - changeNode1.receive(msg); - }); - }); - - it('sets the value of a message property using a nested property in flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(2); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("lookup",{a:1, b:2}); - changeNode1.receive({payload: "", topic: "b"}); - }); - }) - - it('sets the value of a message property using a nested property in flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(2); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("lookup",{a:1, b:2}); - changeNode1.receive({payload: "", topic: "b"}); - }); - }) - - it('sets the value of a nested flow context property using a message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"lookup[msg.topic]","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("newValue"); - changeNode1.context().flow.get("lookup.b").should.eql("newValue"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("lookup",{a:1, b:2}); - changeNode1.receive({payload: "newValue", topic: "b"}); - }); - }) - - it('deep copies the property if selected', function(done) { - - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"set","p":"payload","pt":"msg","to":"source","tot":"msg","dc":true}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - // Check payload has been set to a clone of original object - // - the JSON should match - JSON.stringify(msg.payload).should.equal(JSON.stringify(originalObject)) - // - but they must be different objects - msg.payload.should.not.equal(originalObject); - - // Modify nested property of original object - originalObject.a.c = 3; - // Check that modification hasn't happened on cloned prop - msg.payload.a.should.not.have.property('c'); - - done(); - } catch(err) { - done(err); - } - }); - var originalObject = { a: { b: 2 } } - changeNode1.receive({source:originalObject}); - }); - - }) - }); - describe('#change', function() { - it('changes the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Goodbye World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value and doesnt change type of the message property for partial match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "msg", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Change456Me"); - var t = typeof(msg.payload); - t.should.equal("string"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Change123Me"}); - }); - }); - - it('changes the value and type of the message property if a complete match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "msg", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(456); - var t = typeof(msg.payload); - t.should.equal("number"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123"}); - }); - }); - - it('changes the value of a multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo.bar","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("Goodbye World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({foo:{bar:"Hello World!"}}); - }); - }); - - it('sends unaltered message if the changed message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('sends unaltered message if a changed multi-level message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo.bar","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value of the message property based on a regex', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\d+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Replace all numbers NUMBER and NUMBER"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Replace all numbers 12 and 14"}); - }); - }); - - it('supports regex groups', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"(Hello)","to":"$1-$1-$1","reg":true,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello-Hello-Hello World"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World"}); - }); - }); - - it('reports invalid regex', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\+**+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "change"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'changeNode1'); - done(); - - }); - }); - - it('supports regex groups - new rule format', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"(Hello)","to":"$1-$1-$1","fromt":"re","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello-Hello-Hello World"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World"}); - }); - }); - - it('changes the value - new rule format', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"ABC","to":"123","fromt":"str","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - - it('changes the value using msg property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"msg","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"abcABCabc",topic:"ABC"}); - }); - }); - - it('changes the value using flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"flow","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("topic","ABC"); - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - - it('changes the value using persistable flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"#:(memory1)::topic","to":"123","fromt":"flow","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("topic","ABC","memory1", function (err) { - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - }); - }); - - it('changes the value using global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic","ABC"); - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - - it('changes the value using persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"#:(memory1)::topic","to":"123","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic","ABC","memory1",function (err) { - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - }); - }); - - it('changes the number using global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"ABC","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("ABC"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic",123); - changeNode1.receive({payload:123}); - }); - }); - - it('changes the number using persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"#:(memory1)::topic","to":"ABC","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("ABC"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic",123,"memory1",function (err) { - changeNode1.receive({payload:123}); - }); - }); - }); - }); - - it('changes the value using number - string payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"123","to":"456","fromt":"num","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("456"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123"}); - }); - }); - - it('changes the value using number - number payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"123","to":"abc","fromt":"num","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:123}); - }); - }); - - it('changes the value using boolean - string payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"true","to":"xxx","fromt":"bool","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("xxx"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"true"}); - }); - }); - - it('changes the value using boolean - boolean payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"true","to":"xxx","fromt":"bool","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("xxx"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:true}); - }); - }); - - it('changes the value of the global context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "global", "from": "Hello", "fromt": "str", "to": "Goodbye", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().global.get("payload").should.equal("Goodbye World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("payload","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value of the persistable global context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "global", "from": "Hello", "fromt": "str", "to": "Goodbye", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().global.get("payload","memory1", function (err, val) { - val.should.equal("Goodbye World!"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("payload","Hello World!","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value and doesnt change type of the flow context for partial match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal("Change456Me"); - helperNode1.context().flow.get("payload").should.be.a.String(); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","Change123Me"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value and doesnt change type of the persistable flow context for partial match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.equal("Change456Me"); - val.should.be.a.String(); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","Change123Me","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value and type of the flow context if a complete match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal(456); - helperNode1.context().flow.get("payload").should.be.a.Number(); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","123"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value and type of the persistable flow context if a complete match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.be.a.Number(); - val.should.equal(456); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","123","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value using number - number flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "num", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal("abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",123); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value using number - number persistable flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "123", "fromt": "num", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.equal("abc"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",123,"memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value using boolean - boolean flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "true", "fromt": "bool", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal("abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",true); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value using boolean - boolean persistable flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "true", "fromt": "bool", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.equal("abc"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",true,"memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('reports invalid fromValue', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"null","fromt":"msg","to":"abc","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "change"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'changeNode1'); - done(); - },25); - changeNode1.receive({payload:"",null:null}); - }); - }); - - describe('env var', function() { - before(function() { - process.env.NR_TEST_A = 'foo'; - }) - after(function() { - delete process.env.NR_TEST_A; - }) - it('changes the value using env property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"NR_TEST_A","fromt":"msg","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abcfooabc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"abcABCabc",topic:"ABC"}); - }); - }); - }); - - }); - - describe("#delete", function() { - it('deletes the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"payload","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('payload'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"This won't get through!"}); - }); - }); - - it('deletes the value of global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "delete", "p": "globalValue", "pt": "global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.should.not.have.property("globalValue"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('deletes the value of persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "delete", "p": "#:(memory1)::globalValue", "pt": "global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.get("globalValue","memory1",function(err,val) { - should.equal(undefined); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('deletes the value of a multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo.bar","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('foo.bar'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"This won't get through!", foo:{bar:"This will be deleted!"}}); - }); - }); - - it('sends unaltered message if the deleted message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('foo'); - msg.payload.should.equal('payload'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"payload"}); - }); - }); - - it('sends unaltered message if a deleted multi-level message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo.bar","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('foo.bar'); - msg.payload.should.equal('payload'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"payload"}); - }); - }); - }); - - describe("#move", function() { - it('moves the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('topic'); - msg.should.have.property('payload'); - msg.payload.should.equal("You've got to move it move it."); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({topic:"You've got to move it move it.", payload:{foo:"bar"}}); - }); - }); - it('moves the value of a message property object', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('topic'); - msg.should.have.property('payload'); - msg.payload.should.have.property('foo'); - msg.payload.foo.should.have.property('bar'); - msg.payload.foo.bar.should.equal(1); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({topic:{foo:{bar:1}}, payload:"String"}); - }); - }); - it('moves the value of a message property object to a sub-property', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.foo","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.have.property('foo'); - msg.payload.foo.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"bar"}); - }); - }); - it('moves the value of a message sub-property object to a property', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload.foo","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.equal("bar"); - (typeof msg.payload).should.equal("string"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:{foo:"bar"}}); - }); - }); - }); - - describe('- multiple rules', function() { - it('handles multiple rules', function(done) { - var flow = [{"id":"changeNode1","type":"change","wires":[["helperNode1"]], - rules:[ - {t:"set",p:"payload",to:"newValue"}, - {t:"change",p:"changeProperty",from:"this",to:"that"}, - {t:"delete",p:"deleteProperty"} - ]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("newValue"); - msg.changeProperty.should.equal("change that value"); - should.not.exist(msg.deleteProperty); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({ - payload:"changeMe", - changeProperty:"change this value", - deleteProperty:"delete this value" - }); - }); - }); - - it('applies multiple rules in order', function(done) { - var flow = [{"id":"changeNode1","type":"change","wires":[["helperNode1"]], - rules:[ - {t:"set",p:"payload",to:"a this (hi)"}, - {t:"change",p:"payload",from:"this",to:"that"}, - {t:"change",p:"payload",from:"\\(.*\\)",to:"[new]",re:true}, - ]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("a that [new]"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({ - payload:"changeMe" - }); - }); - }); - - it('can access two persistable flow context property', function(done) { - var flow = [{"id":"changeNode1", "z":"t1", "type":"change", - "wires":[["helperNode1"]], - rules:[ - {"t":"set", "p":"val0", "to":"#:(memory0)::val", "tot":"flow"}, - {"t":"set", "p":"val1", "to":"#:(memory1)::val", "tot":"flow"} - ]}, - {id:"helperNode1", "z":"t1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("val0", "foo"); - msg.should.have.property("val1", "bar"); - done(); - } catch(err) { - done(err); - } - }); - var flow = changeNode1.context().flow; - flow.set("val", "foo", "memory0", function (err) { - flow.set("val", "bar", "memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - }); - - it('can access two persistable global context property', function(done) { - var flow = [{"id":"changeNode1", "z":"t1", "type":"change", - "wires":[["helperNode1"]], - rules:[ - {"t":"set", "p":"val0", "to":"#:(memory0)::val", "tot":"global"}, - {"t":"set", "p":"val1", "to":"#:(memory1)::val", "tot":"global"} - ]}, - {id:"helperNode1", "z":"t1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("val0", "foo"); - msg.should.have.property("val1", "bar"); - done(); - } catch(err) { - done(err); - } - }); - var global = changeNode1.context().global; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - }); - - it('can access persistable global & flow context property', function(done) { - var flow = [{"id":"changeNode1", "z":"t1", "type":"change", - "wires":[["helperNode1"]], - rules:[ - {"t":"set", "p":"val0", "to":"#:(memory0)::val", "tot":"flow"}, - {"t":"set", "p":"val1", "to":"#:(memory1)::val", "tot":"global"} - ]}, - {id:"helperNode1", "z":"t1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("val0", "foo"); - msg.should.have.property("val1", "bar"); - done(); - } catch(err) { - done(err); - } - }); - var context = changeNode1.context(); - var flow = context.flow; - var global = context.global; - flow.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - }); - - }); -}); diff --git a/test/nodes/core/function/16-range_spec.js b/test/nodes/core/function/16-range_spec.js deleted file mode 100644 index a0dcd0078..000000000 --- a/test/nodes/core/function/16-range_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var rangeNode = require("nr-test-utils").require("@node-red/nodes/core/function/16-range.js"); -var helper = require("node-red-node-test-helper"); - -describe('range Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - helper.unload(); - helper.stopServer(done); - }); - - it('should load some defaults', function(done) { - var flow = [{"id":"rangeNode1","type":"range","name":"rangeNode"}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - rangeNode1.should.have.property('name', 'rangeNode'); - rangeNode1.should.have.property('round', false); - done(); - }); - }); - - /** - * Run a generic range test - * @param action - scale/clamp (range limit)/roll (modulo): what action to choose - * @param minin - map from minimum value - * @param maxin - map from maximum value - * @param minout - map to minimum value - * @param maxout - map to maximum value - * @param round - whether to round the result to the nearest integer - * @param aPayload - what payload to send to the range node - * @param expectedResult - what result we're expecting - * @param done - the callback to call when test done - */ - function genericRangeTest(action, minin, maxin, minout, maxout, round, aPayload, expectedResult, done) { - var flow = [{"id":"rangeNode1","type":"range","minin":minin,"maxin":maxin,"minout":minout,"maxout":maxout,"action":action,"round":round,"name":"rangeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(expectedResult); - done(); - } catch(err) { - done(err); - } - }); - rangeNode1.receive({payload:aPayload}); - }); - } - - it('ranges numbers up tenfold', function(done) { - genericRangeTest("scale", 0, 100, 0, 1000, false, 50, 500, done); - }); - - it('ranges numbers down such as centimetres to metres', function(done) { - genericRangeTest("scale", 0, 100, 0, 1, false, 55, 0.55, done); - }); - - it('wraps numbers down say for degree/rotation reading 1/2', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, 15, 180, done); // 1/2 around wrap => "one and a half turns" - }); - - it('wraps numbers around say for degree/rotation reading 1/3', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, 13.3333, 120, done); // 1/3 around wrap => "one and a third turns" - }); - - it('wraps numbers around say for degree/rotation reading 1/4', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, 12.5, 90, done); // 1/4 around wrap => "one and a quarter turns" - }); - - it('wraps numbers down say for degree/rotation reading 1/4', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, -12.5, 270, done); // 1/4 backwards wrap => "one and a quarter turns backwards" - }); - - it('wraps numbers around say for degree/rotation reading 0', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, -10, 0, done); - }); - - it('clamps numbers within a range - over max', function(done) { - genericRangeTest("clamp", 0, 10, 0, 1000, false, 111, 1000, done); - }); - - it('clamps numbers within a range - below min', function(done) { - genericRangeTest("clamp", 0, 10, 0, 1000, false, -1, 0, done); - }); - - it('just passes on msg if payload not present', function(done) { - var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":100,"minout":0,"maxout":100,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('payload'); - msg.topic.should.equal("pass on"); - done(); - } catch(err) { - done(err); - } - }); - rangeNode1.receive({topic:"pass on"}); - }); - }); - - it('reports if input is not a number', function(done) { - var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":0,"minout":0,"maxout":0,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - rangeNode1.on("call:log",function(args) { - var log = args.args[0]; - if (log.indexOf("notnumber") > -1) { - rangeNode1.log.restore(); - done(); - } else { - try { - should.fail(null, null, "Non-number inputs should be reported!"); - } catch (err) { - rangeNode1.log.restore(); - done(err); - } - } - }); - - rangeNode1.receive({payload:"NOT A NUMBER"}); - }); - }); -}); diff --git a/test/nodes/core/function/80-template_spec.js b/test/nodes/core/function/80-template_spec.js deleted file mode 100644 index e944824b3..000000000 --- a/test/nodes/core/function/80-template_spec.js +++ /dev/null @@ -1,498 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var templateNode = require("nr-test-utils").require("@node-red/nodes/core/function/80-template.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); - -describe('template node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - beforeEach(function(done) { - done(); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { // do not use (for excluding effect fallback) - module: "memory" - }, - memory1: { - module: "memory" - }, - memory2: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function() { - helper.unload().then(function () { - return Context.clean({allNodes:{}}); - }).then(function () { - return Context.close(); - }); - }); - - - it('should modify payload using node-configured template', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - msg.should.have.property('template', 'this should be ignored as the node has its own template {{payload}}'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar", template: "this should be ignored as the node has its own template {{payload}}"}); - }); - }); - - it('should modify the configured property using msg.template', function(done) { - var flow = [{id:"n1", type:"template", field:"randomProperty", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - msg.should.have.property('template', 'payload={{payload}}'); - msg.should.have.property('randomProperty', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo", topic: "bar", template: "payload={{payload}}"}); - }); - }); - - it('should be able to overwrite msg.template using the template from msg.template', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'topic=bar'); - msg.should.have.property('template', 'topic={{topic}}'); - done(); - }); - n1.receive({payload:"foo", topic: "bar", template: "topic={{topic}}"}); - }); - }); - - it('should modify payload from msg.template', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var received = []; - n2.on("input", function(msg) { - try { - received.push(msg); - if (received.length === 3) { - received[0].should.have.property('topic', 'bar'); - received[0].should.have.property('payload', 'topic=bar'); - received[0].should.have.property('template', 'topic={{topic}}'); - - received[1].should.have.property('topic', 'another bar'); - received[1].should.have.property('payload', 'topic=another bar'); - received[1].should.have.property('template', 'topic={{topic}}'); - - received[2].should.have.property('topic', 'bar'); - received[2].should.have.property('payload', 'payload=foo'); - received[2].should.have.property('template', 'payload={{payload}}'); - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", topic: "bar", template: "topic={{topic}}"}); - n1.receive({payload:"foo", topic: "another bar", template: "topic={{topic}}"}); - n1.receive({payload:"foo", topic: "bar", template: "payload={{payload}}"}); - }); - }); - - it('should modify payload from flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("value","foo"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify payload from persistable flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow[memory1].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.context().flow.set("value","foo","memory1",function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - - it('should handle nested context tags - property not set', function(done) { - // This comes from the Coursera Node-RED course and is a good example of - // multiple conditional tags - var template = `{{#flow.time}}time={{flow.time}}{{/flow.time}}{{^flow.time}}!time{{/flow.time}}{{#flow.random}}random={{flow.random}}randomtime={{flow.randomtime}}{{/flow.random}}{{^flow.random}}!random{{/flow.random}}`; - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:template,wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '!time!random'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }) - it('should handle nested context tags - property set', function(done) { - // This comes from the Coursera Node-RED course and is a good example of - // multiple conditional tags - var template = `{{#flow.time}}time={{flow.time}}{{/flow.time}}{{^flow.time}}!time{{/flow.time}}{{#flow.random}}random={{flow.random}}randomtime={{flow.randomtime}}{{/flow.random}}{{^flow.random}}!random{{/flow.random}}`; - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:template,wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'time=123random=456randomtime=789'); - done(); - } catch(err) { - done(err); - } - }); - n1.context().flow.set(["time","random","randomtime"],["123","456","789"],function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }) - - it('should modify payload from two persistable flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow[memory1].value}}/{{flow[memory2].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo/bar'); - done(); - }); - n1.context().flow.set("value","foo","memory1",function (err) { - n1.context().flow.set("value","bar","memory2",function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - }); - - it('should modify payload from global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("value","foo"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify payload from persistable global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global[memory1].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.context().global.set("value","foo","memory1", function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - - it('should modify payload from two persistable global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global[memory1].value}}/{{global[memory2].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo/bar'); - done(); - }); - n1.context().global.set("value","foo","memory1", function (err) { - n1.context().global.set("value","bar","memory2", function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - }); - - it('should modify payload from persistable flow & global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow[memory1].value}}/{{global[memory1].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo/bar'); - done(); - }); - n1.context().flow.set("value","foo","memory1", function (err) { - n1.context().global.set("value","bar","memory1", function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - }); - - it('should handle missing node context', function(done) { - // this is artificial test because in flow there is missing z property (probably never happen in real usage) - var flow = [{id:"n1",type:"template", field:"payload", template:"payload={{flow.value}},{{global.value}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=,'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle escape characters in Mustache format and JSON output mode', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", syntax:"mustache", template:"{\"data\":\"{{payload}}\"}", output:"json", wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.payload.should.have.property('data', 'line\t1\nline\\2\r\nline\b3\f'); - done(); - }); - n1.receive({payload:"line\t1\nline\\2\r\nline\b3\f"}); - }); - }); - - it('should modify payload in plain text mode', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", syntax:"plain", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload={{payload}}'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in flow context - n2.context().flow.get("payload").should.equal("payload=foo"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify persistable flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"#:(memory1)::payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in flow context - n2.context().flow.get("payload", "memory1", function (err, val) { - val.should.equal("payload=foo"); - done(); - }); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should modify global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in global context - n2.context().global.get("payload").should.equal("payload=foo"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify persistable global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"#:(memory1)::payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in global context - n2.context().global.get("payload", "memory1", function (err, val) { - val.should.equal("payload=foo"); - done(); - }); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should handle if the field isn\'t set', function(done) { - var flow = [{id:"n1", type:"template", template: "payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle deeper objects', function(done) { - var flow = [{id:"n1", type:"template", field: "topic.foo.bar", template: "payload={{payload.doh.rei.me}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic'); - msg.topic.should.have.property('foo'); - msg.topic.foo.should.have.a.property('bar', 'payload=foo'); - done(); - }); - n1.receive({payload:{doh:{rei:{me:"foo"}}}}); - }); - }); - - it('should handle block contexts objects', function(done) { - var flow = [{id:"n1", type:"template", template: "A{{#payload.A}}{{payload.A}}{{.}}{{/payload.A}}B",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload','AabcabcB'); - done(); - }); - n1.receive({payload:{A:"abc"}}); - }); - }); - - it('should raise error if passed bad template', function(done) { - var flow = [{id:"n1", type:"template", field: "payload", template: "payload={{payload",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "template"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Unclosed tag at "); - done(); - },25); - n1.receive({payload:"foo"}); - }); - }); - -}); diff --git a/test/nodes/core/function/89-delay_spec.js b/test/nodes/core/function/89-delay_spec.js deleted file mode 100644 index 17958b95b..000000000 --- a/test/nodes/core/function/89-delay_spec.js +++ /dev/null @@ -1,1004 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var delayNode = require("nr-test-utils").require("@node-red/nodes/core/function/89-delay.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); - -var GRACE_PERCENTAGE=10; - -var nanosToSeconds = 1000000000; -var millisToSeconds = 1000; - -var secondsToMinutes = 60; -var secondsToHours = 3600; -var secondsToDays = 86400; - -describe('delay Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - RED.settings.nodeMessageBufferMaxLength = 0; - helper.unload(); - helper.stopServer(done); - }); - - it('should be loaded', function(done) { - var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"day","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - delayNode1.should.have.property('name', 'delayNode'); - delayNode1.should.have.property('rate', 86400000); - done(); - }); - }); - - it('should be able to set rate to hour', function(done) { - var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"hour","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - delayNode1.should.have.property('name', 'delayNode'); - delayNode1.should.have.property('rate', 3600000); - done(); - }); - }); - - it('should be able to set rate to minute', function(done) { - var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - delayNode1.should.have.property('name', 'delayNode'); - delayNode1.should.have.property('rate', 60000); - done(); - }); - }); - - var TimeUnitEnum = { - MILLIS : "milliseconds", - SECONDS : "seconds", - MINUTES : "minutes", - HOURS : "hours", - DAYS : "days" - } - - /** - * Tells whether two numeric values are close enough to each other - * @param actualValue - the value we're testing - * @param expectedValue - the value we're matching the test value against - * @param tolerancePercent - the percentage of tolerated deviation (0 means equals) - */ - function closeEnough(actualValue, expectedValue, tolerancePercent) { - var toReturn; - var toleranceFraction = expectedValue * (tolerancePercent/100); - var minExpected = expectedValue - toleranceFraction; - var maxExpected = expectedValue + toleranceFraction; - - if (actualValue >= minExpected && actualValue <= maxExpected) { - toReturn = true; - } else { - toReturn = false; - } - return toReturn; - } - - /** - * Runs a delay test - * @param aTimeout - the timeout quantity - * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days - */ - function genericDelayTest(aTimeout, aTimeoutUnit, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":aTimeout,"timeoutUnits":aTimeoutUnit,"rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var endTime = process.hrtime(startTime); - var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] ); - var runtimeSeconds = runtimeNanos / nanosToSeconds; - var aTimeoutUnifiedToSeconds; - - // calculating the timeout in seconds - if (aTimeoutUnit == TimeUnitEnum.MILLIS) { - aTimeoutUnifiedToSeconds = aTimeout / millisToSeconds; - } else if (aTimeoutUnit == TimeUnitEnum.SECONDS) { - aTimeoutUnifiedToSeconds = aTimeout; - } else if (aTimeoutUnit == TimeUnitEnum.MINUTES) { - aTimeoutUnifiedToSeconds = aTimeout * secondsToMinutes; - } else if (aTimeoutUnit == TimeUnitEnum.HOURS) { - aTimeoutUnifiedToSeconds = aTimeout * secondsToHours; - } else if (aTimeoutUnit == TimeUnitEnum.DAYS) { - aTimeoutUnifiedToSeconds = aTimeout * secondsToDays; - } - - if (closeEnough(runtimeSeconds, aTimeoutUnifiedToSeconds, GRACE_PERCENTAGE)) { - done(); - } else { - try { - should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not close enough to exlected timeout seconds: " + aTimeoutUnifiedToSeconds); - } catch (err) { - done(err); - } - } - } catch(err) { - done(err); - } - }); - var startTime = process.hrtime(); - delayNode1.receive({payload:"delayMe"}); - }); - } - - /** - * We send a message, take a timestamp then when the message is received by the helper node, we take another timestamp. - * Then check if the message has been delayed by the expected amount. - */ - - it('delays the message in seconds', function(done) { - genericDelayTest(0.5, "seconds", done); - }); - - it('delays the message in milliseconds', function(done) { - genericDelayTest(500, "milliseconds", done); - }); - - it('delays the message in minutes', function(done) { // this is also 0.5 seconds - genericDelayTest(0.00833, "minutes", done); - }); - - it('delays the message in hours', function(done) { // this is also 0.5 seconds - genericDelayTest(0.0001388, "hours", done); - }); - - it('delays the message in days', function(done) { // this is also 0.5 seconds - genericDelayTest(0.000005787, "days", done); - }); - - /** - * Runs a rate limit test - only testing seconds! - * @param aLimit - the message limit count - * @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds - * @param runtimeInMillis - when to terminate run and count messages received - */ - function genericRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, rateValue, done) { - var flow = [{"id":"delayNode1","type":"delay","nbRateUnits":nbUnit,"name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var receivedMessagesStack = []; - var rate = 1000 / aLimit * nbUnit; - - var receiveTimestamp; - - helperNode1.on("input", function(msg) { - if (receiveTimestamp) { - var elapse = process.hrtime(receiveTimestamp); - var receiveInterval = (elapse[0] * 1000) + ((elapse[1] / nanosToSeconds) * 1000); - receiveInterval.should.be.above(rate * 0.9); - } - receiveTimestamp = process.hrtime(); - receivedMessagesStack.push(msg); - }); - - var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst - - var i = 0; - for (; i < possibleMaxMessageCount + 1; i++) { - delayNode1.receive({ payload: i, rate: rateValue }); - } - - setTimeout(function() { - try { - receivedMessagesStack.length.should.be.lessThan(possibleMaxMessageCount); - for (var j = 0; j < receivedMessagesStack.length; j++) { - if (receivedMessagesStack[j].payload === j) { - if (j === (receivedMessagesStack.length -1)) { // last message, all matched so far - done(); - } - } else { - should.fail(null, null, "Received messages were not received in order. Message was " + receivedMessagesStack[i].payload + " on count " + i); - } - } - } catch (err) { - done(err); - } - }, runtimeInMillis); - }); - } - - it('limits the message rate to 1 per second', function(done) { - genericRateLimitSECONDSTest(1, 1, 1500, null, done); - }); - - it('limits the message rate to 1 per 2 seconds', function(done) { - this.timeout(6000); - genericRateLimitSECONDSTest(1, 2, 3000, null, done); - }); - - it('limits the message rate to 2 per seconds, 2 seconds', function(done) { - this.timeout(6000); - genericRateLimitSECONDSTest(2, 1, 2100, null, done); - }); - - it('limits the message rate using msg.rate', function (done) { - RED.settings.nodeMessageBufferMaxLength = 3; - genericRateLimitSECONDSTest(1, 1, 1500, 2000, done); - }); - - /** - * Runs a rate limit test with drop support - only testing seconds! - * @param aLimit - the message limit count - * @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds - * @param runtimeInMillis - when to terminate run and count messages received - */ - function dropRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, rateValue, sendIntermediate, done) { - if (!done) { - done = sendIntermediate; - sendIntermediate = false; - } - var outputs = 1; - if (sendIntermediate) { - outputs = 2; - } - var flow = [ - {"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"nbRateUnits":nbUnit,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,outputs:outputs,"wires":[["helperNode1"],["helperNode2"]]}, - {id:"helperNode1", type:"helper", wires:[]}, - {id:"helperNode2", type:"helper", wires:[]} - ] - - ; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var helperNode2 = helper.getNode("helperNode2"); - var receivedMessagesStack = []; - var receivedIntermediateMessagesStack = []; - - // Add a small grace to the calculated delay - var rate = 1000/aLimit + 10; - - var receiveTimestamp; - - helperNode1.on("input", function(msg) { - if (receiveTimestamp) { - var elapse = process.hrtime(receiveTimestamp); - var receiveInterval = (elapse[0] * 1000) + ((elapse[1] / nanosToSeconds) * 1000); - receiveInterval.should.be.above(rate * 0.9); - } - receiveTimestamp = process.hrtime(); - receivedMessagesStack.push(msg); - }); - helperNode2.on("input", function(msg) { - receivedIntermediateMessagesStack.push(msg); - }); - - var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst - - var i = 0; - delayNode1.receive({ payload: i, rate: rateValue }); - i++; - for (; i < possibleMaxMessageCount + 1; i++) { - setTimeout(function() { - delayNode1.receive({payload:i}); - }, 2 * ((rate * i) / possibleMaxMessageCount) ); - } - - //we need to send a message delayed so that it doesn't get dropped - setTimeout(function() { - delayNode1.receive({payload:++i}); - }, runtimeInMillis - 300); // should give enough time to squeeze another message in - - setTimeout(function() { - try { - receivedMessagesStack.length.should.be.lessThan(possibleMaxMessageCount + 1); - receivedMessagesStack.length.should.be.greaterThan(2); // ensure that we receive more than 1st and last message - receivedMessagesStack[0].payload.should.be.exactly(0); // means we received the last message injected just before test termination - var foundAtLeastOneDrop = false; - for (var i = 0; i < receivedMessagesStack.length; i++) { - if (i > 0) { - if (receivedMessagesStack[i].payload - receivedMessagesStack[i - 1].payload > 1) { - foundAtLeastOneDrop = true; - } - } - } - foundAtLeastOneDrop.should.be.true(); - if (sendIntermediate) { - receivedIntermediateMessagesStack.length.should.be.greaterThan(0); - } else { - receivedIntermediateMessagesStack.length.should.be.exactly(0); - } - done(); - } catch (err) { - done(err); - } - }, runtimeInMillis); - }); - } - - it('limits the message rate to 1 per second, 4 seconds, with drop', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(1, 1, 4000, null, done); - }); - - it('limits the message rate to 1 per 2 seconds, 4 seconds, with drop', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(1, 2, 4500, null, done); - }); - - it('limits the message rate to 2 per second, 5 seconds, with drop', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(2, 1, 5000, null, done); - }); - - it('limits the message rate to 2 per second, 5 seconds, with drop, 2nd output', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(2, 1, 5000, null, true, done); - }); - - it('limits the message rate with drop using msg.rate', function (done) { - this.timeout(6000); - RED.settings.nodeMessageBufferMaxLength = 3; - dropRateLimitSECONDSTest(2, 1, 5000, 1000, done); - }); - - /** - * Returns true if the actualTimeout is gracefully in between the timeoutFrom and timeoutTo - * values. Gracefully means that inBetween could actually mean smaller/greater values - * than the timeout range so long as it's within an actual grace percentage. - * @param timeoutFrom - The expected timeout range (low number) - * @param timeoutTo - The expected timeout range (high number) - * @param actualTimeout - The actual measured timeout value of test - * @param allowedGracePercent - The percentage of grace allowed - */ - function inBetweenDelays(timeoutFrom, timeoutTo, actualTimeout, allowedGracePercent) { - if (closeEnough(actualTimeout, timeoutFrom, allowedGracePercent)) { - return true; - } else if (closeEnough(actualTimeout, timeoutTo, allowedGracePercent)) { - return true; - } else if (timeoutFrom < actualTimeout && timeoutTo > actualTimeout) { - return true; - } else { - return false; - } - } - - /** - * Runs a VARIABLE DELAY test, checks if the delay is in between the given timeout values - * @param aTimeoutFrom - the timeout quantity which is the minimal acceptable wait period - * @param aTimeoutTo - the timeout quantity which is the maximum acceptable wait period - * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days - * @param delay - the variable delay: milliseconds - */ - function variableDelayTest(aTimeoutFrom, aTimeoutTo, aTimeoutUnit, delay, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delayv","timeout":0.5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var endTime = process.hrtime(startTime); - var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] ); - var runtimeSeconds = runtimeNanos / nanosToSeconds; - var aTimeoutFromUnifiedToSeconds; - var aTimeoutToUnifiedToSeconds; - - // calculating the timeout in seconds - if (aTimeoutUnit == TimeUnitEnum.MILLIS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom / millisToSeconds; - aTimeoutToUnifiedToSeconds = aTimeoutTo / millisToSeconds; - } else if (aTimeoutUnit == TimeUnitEnum.SECONDS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom; - aTimeoutToUnifiedToSeconds = aTimeoutTo; - } else if (aTimeoutUnit == TimeUnitEnum.MINUTES) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToMinutes; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToMinutes; - } else if (aTimeoutUnit == TimeUnitEnum.HOURS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToHours; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToHours; - } else if (aTimeoutUnit == TimeUnitEnum.DAYS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToDays; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToDays; - } - - if (inBetweenDelays(aTimeoutFromUnifiedToSeconds, aTimeoutToUnifiedToSeconds, runtimeSeconds, GRACE_PERCENTAGE)) { - done(); - } else { - try { - should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not \"in between enough\" enough to expected values of: " + aTimeoutFromUnifiedToSeconds + " and " + aTimeoutToUnifiedToSeconds); - } catch (err) { - done(err); - } - } - } catch(err) { - done(err); - } - }); - var startTime = process.hrtime(); - delayNode1.receive({payload:"delayMe", delay:delay}); - }); - } - - it('variable delay set by msg.delay the message in milliseconds', function(done) { - variableDelayTest("200", "300", "milliseconds", 250, done); - }); - - it('variable delay is the default if msg.delay not specified', function(done) { - variableDelayTest("450", "550", "milliseconds", null, done); - }); - - it('variable delay is zero if msg.delay is zero', function(done) { - variableDelayTest("0", "20", "milliseconds", 0, done); - }); - - it('variable delay is zero if msg.delay is negative', function(done) { - variableDelayTest("0", "20", "milliseconds", -250, done); - }); - - /** - * Runs a RANDOM DELAY test, checks if the delay is in between the given timeout values - * @param aTimeoutFrom - the timeout quantity which is the minimal acceptable wait period - * @param aTimeoutTo - the timeout quantity which is the maximum acceptable wait period - * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days - */ - function randomDelayTest(aTimeoutFrom, aTimeoutTo, aTimeoutUnit, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"random","timeout":5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var endTime = process.hrtime(startTime); - var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] ); - var runtimeSeconds = runtimeNanos / nanosToSeconds; - var aTimeoutFromUnifiedToSeconds; - var aTimeoutToUnifiedToSeconds; - - // calculating the timeout in seconds - if (aTimeoutUnit == TimeUnitEnum.MILLIS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom / millisToSeconds; - aTimeoutToUnifiedToSeconds = aTimeoutTo / millisToSeconds; - } else if (aTimeoutUnit == TimeUnitEnum.SECONDS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom; - aTimeoutToUnifiedToSeconds = aTimeoutTo; - } else if (aTimeoutUnit == TimeUnitEnum.MINUTES) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToMinutes; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToMinutes; - } else if (aTimeoutUnit == TimeUnitEnum.HOURS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToHours; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToHours; - } else if (aTimeoutUnit == TimeUnitEnum.DAYS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToDays; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToDays; - } - - if (inBetweenDelays(aTimeoutFromUnifiedToSeconds, aTimeoutToUnifiedToSeconds, runtimeSeconds, GRACE_PERCENTAGE)) { - done(); - } else { - try { - should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not \"in between enough\" enough to expected values of: " + aTimeoutFromUnifiedToSeconds + " and " + aTimeoutToUnifiedToSeconds); - } catch (err) { - done(err); - } - } - } catch(err) { - done(err); - } - }); - var startTime = process.hrtime(); - delayNode1.receive({payload:"delayMe"}); - }); - } - - it('randomly delays the message in seconds', function(done) { - randomDelayTest(0.4, 0.8, "seconds", done); - }); - - it('randomly delays the message in milliseconds', function(done) { - randomDelayTest("400", "800", "milliseconds", done); - }); - - it('randomly delays the message in minutes', function(done) { - randomDelayTest(0.0066, 0.0133, "minutes", done); - }); - - it('delays the message in hours', function(done) { - randomDelayTest(0.000111111, 0.000222222, "hours", done); - }); - - it('delays the message in days', function(done) { - randomDelayTest(0.0000046296, 0.0000092593, "days", done); - }); - - it('handles delay queue', function(done) { - this.timeout(2000); - var flow = [{id:"delayNode1", type :"delay","name":"delayNode","nbRateUnits":"1","pauseType":"queue","timeout":1,"timeoutUnits":"seconds","rate":4,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "_none_") { - msg.payload.should.equal(2); - (Date.now() - t).should.be.approximately(500,200); - } - else if (msg.topic === "A") { - msg.payload.should.equal(4); - (Date.now() - t).should.be.approximately(750,200); - } - else { - msg.topic.should.equal("B"); - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(1000,200); - done(); - } - } catch(e) { - done(e); - } - }); - setTimeout(function() { - // send test messages - delayNode1.receive({payload:1}); // send something with blank topic - delayNode1.receive({payload:1,topic:"A"}); // and something with a fixed topic - delayNode1.receive({payload:1,topic:"B"}); // and something else with a fixed topic (3rd tick) - delayNode1.receive({payload:2,topic:"A"}); // these should replace them in queue - delayNode1.receive({payload:3,topic:"A"}); // ditto - delayNode1.receive({payload:2}); // so only this should get out on first tick - delayNode1.receive({payload:4,topic:"A"}); // and this one on second tick - }, 275); // wait one tick beofre starting.. (to test no messages in queue path.) - }); - }); - - it('handles timed queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"timed","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "_none_") { - msg.payload.should.equal(2); - (Date.now() - t).should.be.approximately(500,200); - } - else if (msg.topic === "A") { - msg.payload.should.equal(4); - (Date.now() - t).should.be.approximately(500,200); - } - else { - msg.topic.should.equal("B"); - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(500,200); - done(); - } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1}); // send something with blank topic - delayNode1.receive({payload:1,topic:"A"}); // and something with a fixed topic - delayNode1.receive({payload:1,topic:"B"}); // and something else with a fixed topic - delayNode1.receive({payload:2,topic:"A"}); // these should replace them in queue - delayNode1.receive({payload:3,topic:"A"}); // ditto - delayNode1.receive({payload:2}); // so all should go on first tick - delayNode1.receive({payload:4,topic:"A"}); // and nothing on second - }); - }); - - it('can flush delay queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else { - if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({flush:true}); }); // reset the queue - }); - }); - - it('can part flush delay queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(200,100); - c = c + 1; - } - else if (msg.topic === "boo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(400,100); - c = c + 1; - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); - setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({flush:2}); }); - setTimeout( function() { delayNode1.receive({flush:1}); }, 200); - setTimeout( function() { delayNode1.receive({flush:4}); }, 400); - }); - }); - - it('can reset delay queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - c = c + 1; - }); - - setTimeout( function() { - if (c === 0) { done(); } - }, 700); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({reset:true}); }); // reset the queue - }); - }); - - it('can flush rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else { - if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({flush:true}); }); // reset the queue - }); - }); - - it('can part flush rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(200,100); - c = c + 1; - } - else if (msg.topic === "boo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(400,100); - c = c + 1; - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); - setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({flush:2}); }); - setTimeout( function() { delayNode1.receive({flush:1}); }, 200); - setTimeout( function() { delayNode1.receive({flush:4}); }, 400); - }); - }); - - it('can part push to front of rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":1,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "aoo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(2,50); - c = c + 1; - } - else if (msg.topic === "eoo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(4,50); - c = c + 1; - } - else if (msg.topic === "coo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(202,50); - c = c + 1; - } - else if (msg.topic === "boo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(406,50); - c = c + 1; - } - else if (msg.topic === "doo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(4,50); - c = c + 1; - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"aoo"}); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"coo",toFront:true}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"doo",toFront:true,flush:1}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"eoo",toFront:true}); } ); - setImmediate( function() { delayNode1.receive({flush:1}); }); - setTimeout( function() { delayNode1.receive({flush:1}); }, 200); - setTimeout( function() { delayNode1.receive({flush:4}); }, 400); - }); - }); - - it('can reset rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - } catch(e) { - done(e); - } - }); - - setTimeout( function() { - if (c === 1) { done(); } - }, 700); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({reset:true}); }); // reset the queue - }); - }); - - /* Messaging API support */ - function mapiDoneTestHelper(done, pauseType, drop, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const flow = [{id:"delayNode1",type:"delay",name:"delayNode", pauseType:pauseType, timeout:"1", timeoutUnits: "seconds", - rate: "1", nbRateUnits: "1", rateUnits: "second", randomFirst:"950", randomLast:"1050",randomUnits:"milliseconds", - drop: drop, wires: [[]]}, - {id:"completeNode1",type:"complete",scope: ["delayNode1"],uncaught:false,wires:[["helperNode1"]]}, - {id:"helperNode1",type:"helper", wires:[[]]}]; - const numMsgs = msgAndTimings.length; - helper.load([delayNode, completeNode], flow, function () { - const delayNode1 = helper.getNode("delayNode1"); - const helperNode1 = helper.getNode("helperNode1"); - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload', msgAndTimings[c].msg.payload); - (Date.now() - t).should.be.approximately(msgAndTimings[c].avr, msgAndTimings[c].var); - c += 1; - if ( c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setImmediate( function() { delayNode1.receive(msgAndTimings[i].msg); } ); - } - }); - } - - it('calls done when queued message is emitted (type: delay)', function(done) { - mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:1000, var:100}]); - }); - it('calls done when queued message is emitted (type: delayv)', function(done) { - mapiDoneTestHelper(done, "delayv", false, [{msg:{payload:1, delay:1000}, avr:1000, var:100}]); - }); - it('calls done when queued message is emitted (type: delay)', function(done) { - mapiDoneTestHelper(done, "random", false, [{msg:{payload:1}, avr:1000, var:100}]); - }); - it('calls done when queued message is cleared (type: delay)', function(done) { - mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,reset:true}, avr:100, var:100}]); - }); - it('calls done when queued message is cleared (type: delayv)', function(done) { - mapiDoneTestHelper(done, "delayv", false, [{msg:{payload:1, delay:1000}, avr:100, var:100}, - {msg:{payload:2, reset:true}, avr:100, var:100}]); - }); - it('calls done when queued message is cleared (type: random)', function(done) { - mapiDoneTestHelper(done, "random", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,reset:true}, avr:100, var:100}]); - }); - it('calls done when queued message is flushed (type: delay)', function(done) { - mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,flush:true}, avr:100, var:100}]); - }); - it('calls done when queued message is flushed (type: delayv)', function(done) { - mapiDoneTestHelper(done, "delayv", false, [{msg:{payload:1, delay:1000}, avr:100, var:100}, - {msg:{payload:2, flush:true}, avr:100, var:100}]); - }); - it('calls done when queued message is flushed (type: random)', function(done) { - mapiDoneTestHelper(done, "random", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,flush:true}, avr:100, var:100}]); - }); - it('calls done when rated message is emitted (drop: false)', function(done) { - mapiDoneTestHelper(done, "rate", false, [{msg:{payload:1}, avr:0, var:100}, - {msg:{payload:2}, avr:1000, var:100}]); - }); - it('calls done when rated message is emitted (drop: true)', function(done) { - mapiDoneTestHelper(done, "rate", true, [{msg:{payload:1}, avr:0, var:100}, - {msg:{payload:2}, avr:0, var:100}]); - }); - it('calls done when rated message is flushed', function(done) { - mapiDoneTestHelper(done, "rate", false, [{msg:{payload:1}, avr:0, var:100}, - {msg:{payload:2}, avr:0, var:100}, - {msg:{payload:3,flush:true}, avr:0, var:100}]); - }); - it('calls done when queued messages are sent (queue)', function(done) { - this.timeout(3000); - mapiDoneTestHelper(done, "queue", false, [{msg:{payload:1,topic:"A"}, avr:1000, var:700}, - {msg:{payload:2,topic:"B"}, avr:2000, var:700}]); - }); - it('calls done when queued messages are sent (timed)', function(done) { - mapiDoneTestHelper(done, "timed", false, [{msg:{payload:1,topic:"a"}, avr:500, var:700}, - {msg:{payload:2,topic:"b"}, avr:500, var:700}]); - }); - it('calls done when queue is reset (queue/timed)', function(done) { - mapiDoneTestHelper(done, "timed", false, [{msg:{payload:1,topic:"a"}, avr:0, var:500}, - {msg:{payload:2,reset:true}, avr:0, var:500}]); - }); - it('calls done when queue is flushed (queue/timed)', function(done) { - mapiDoneTestHelper(done, "timed", false, [{msg:{payload:1,topic:"a"}, avr:0, var:500}, - {msg:{payload:2,flush:true}, avr:0, var:500}]); - }); -}); diff --git a/test/nodes/core/function/89-trigger_spec.js b/test/nodes/core/function/89-trigger_spec.js deleted file mode 100644 index 33401e540..000000000 --- a/test/nodes/core/function/89-trigger_spec.js +++ /dev/null @@ -1,1198 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var helper = require("node-red-node-test-helper"); -var triggerNode = require("nr-test-utils").require("@node-red/nodes/core/function/89-trigger.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var RED = require("nr-test-utils").require("node-red/lib/red"); - -describe('trigger node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - }, - memory2: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - helper.stopServer(done); - }); - }); - - it("should be loaded with correct defaults", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'triggerNode'); - n1.should.have.property('op1', '1'); - n1.should.have.property('op2', '0'); - n1.should.have.property('op1type', 'str'); - n1.should.have.property('op2type', 'str'); - n1.should.have.property('extend', "false"); - n1.should.have.property('units', 'ms'); - n1.should.have.property('duration', 250); - done(); - }); - }); - - it("should be able to set delay in seconds", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"s", duration:"1", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('duration', 1000); - done(); - }); - }); - - it("should be able to set delay in minutes", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"min", duration:"1", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('duration', 60000); - done(); - }); - }); - - it("should be able to set delay in hours", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"hr", duration:"1", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('duration', 3600000); - done(); - }); - }); - - function basicTest(type, val, rval) { - it('should output 1st value when triggered ('+type+')', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:val, op1type:type, op2:"", op2type:"null", duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - process.env[val] = rval; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - if (rval) { - msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); - } - else { - msg.should.have.property("payload", val); - } - delete process.env[val]; - done(); - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should output 2st value when triggered ('+type+')', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"str", op2:val, op2type:type, duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - process.env[val] = rval; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.property("payload", "foo"); - c++; - } - else { - if (rval) { - msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); - } - else { - msg.should.have.property("payload", val); - } - delete process.env[val]; - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - } - - basicTest("num", 10); - basicTest("str", "10"); - basicTest("bool", true); - var val_json = '{ "x":"vx", "y":"vy", "z":"vz" }'; - basicTest("json", val_json, JSON.parse(val_json)); - var val_buf = "[1,2,3,4,5]"; - basicTest("bin", val_buf, Buffer.from(JSON.parse(val_buf))); - basicTest("env", "NR-TEST", "env-val"); - - it('should output 1 then 0 when triggered (default)', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", '1'); - c+=1; - } - else { - msg.should.have.a.property("payload", '0'); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should ignore any other inputs while triggered if extend is false', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"50",wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var errored = false; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", '1'); - } - else { - msg.should.have.a.property("payload", '0'); - } - c+=1; - }catch(err) { - errored = true; - done(err); - } - }); - setTimeout( function() { - if (!errored) { - try { - c.should.equal(2); - done(); - } catch(err) { - done(err); - } - } - },100); - n1.emit("input", {payload:null}); - setTimeout( function() { - n1.emit("input", {payload:null}); - },10); - setTimeout( function() { - n1.emit("input", {payload:null}); - },30); - }); - }); - - it('should ignore msg.delay if overrideDelay not set', function(done) { - var flow = [ - {"id":"n1", "type":"trigger", "name":"triggerNode", duration:"50",wires:[["n2"]] }, - {id:"n2", type:"helper"} - ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var firstTime; - n2.on("input", function(msg) { - if (c === 0) { - firstTime = Date.now(); - } else if (c === 1) { - try { - var delta = Date.now() - firstTime; - delta.should.be.greaterThan(30); - delta.should.be.lessThan(100); - done(); - } catch(err) { - done(err); - } - } - c++; - }); - n1.emit("input", {payload:null, delay: 300}); - }); - }); - - it('should use msg.delay if overrideDelay is set', function(done) { - var flow = [ - {"id":"n1", "type":"trigger", "name":"triggerNode", overrideDelay: true, duration:"50",wires:[["n2"]] }, - {id:"n2", type:"helper"} - ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var firstTime; - n2.on("input", function(msg) { - if (c === 0) { - firstTime = Date.now(); - } else if (c === 1) { - try { - var delta = Date.now() - firstTime; - delta.should.be.greaterThan(270); - delta.should.be.lessThan(380); - done(); - } catch(err) { - done(err); - } - } - c++; - }); - n1.emit("input", {payload:null, delay: 300}); - }); - }); - - - it('should handle true and false as strings and delay of 0', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"true",op1type:"val",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", true); - c+=1; - } - else { - msg.should.have.a.property("payload", false); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should handle multiple topics as one if not asked to handle', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"all", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "A"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,topic:"A"}); - n1.emit("input", {payload:2,topic:"B"}); - n1.emit("input", {payload:3,topic:"C"}); - }); - }); - - it('should handle multiple topics individually if asked to do so', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "B"); - } - else if (c === 3) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "C"); - } - else if (c === 4) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "A"); - } - else if (c === 5) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "B"); - } - else if (c === 6) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "C"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,topic:"A"}); - n1.emit("input", {payload:2,topic:"B"}); - n1.emit("input", {payload:3,topic:"C"}); - }); - }); - - it('should handle multiple topics individually, and extend one, if asked to do so', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", extend:"true", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "B"); - } - else if (c === 3) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "C"); - } - else if (c === 4) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "A"); - } - else if (c === 5) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "C"); - } - else if (c === 6) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "B"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,topic:"A"}); - n1.emit("input", {payload:2,topic:"B"}); - n1.emit("input", {payload:3,topic:"C"}); - setTimeout( function() { n1.emit("input", {payload:2,topic:"B"})}, 20 ); - }); - }); - - it('should handle multiple other properties individually if asked to do so', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", topic:"foo", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("foo", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("foo", "B"); - } - else if (c === 3) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("foo", "C"); - } - else if (c === 4) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("foo", "A"); - } - else if (c === 5) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("foo", "B"); - } - else if (c === 6) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("foo", "C"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,foo:"A"}); - n1.emit("input", {payload:2,foo:"B"}); - n1.emit("input", {payload:3,foo:"C"}); - }); - }); - - it('should be able to return things from flow and global context variables', function(done) { - var spy = sinon.stub(RED.util, 'evaluateNodeProperty').callsFake( - function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } } - ); - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"flow", op2:"bar", op2type:"global", duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "foo"); - c+=1; - } - else { - msg.should.have.a.property("payload", "bar"); - RED.util.evaluateNodeProperty.restore(); - done(); - } - } - catch(err) { RED.util.evaluateNodeProperty.restore(); done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should be able to return things from persistable flow and global context variables', function (done) { - var flow = [{"id": "n1", "type": "trigger", "name": "triggerNode", "op1": "#:(memory1)::foo", "op1type": "flow", - "op2": "#:(memory1)::bar", "op2type": "global", "duration": "20", "wires": [["n2"]], "z": "flow" }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function (msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "foo"); - c += 1; - } else { - msg.should.have.a.property("payload", "bar"); - done(); - } - } catch (err) { - done(err); - } - }); - var context = n1.context(); - var flow = context.flow; - var global = context.global; - flow.set("foo", "foo", "memory1", function (err) { - global.set("bar", "bar", "memory1", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to return things from multiple persistable global context variables', function (done) { - var flow = [{"id": "n1", "z": "flow", "type": "trigger", - "duration": "20", "wires": [["n2"]], - "op1": "#:(memory1)::val", "op1type": "global", - "op2": "#:(memory2)::val", "op2type": "global" - }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - if (count === 0) { - msg.should.have.a.property("payload", "foo"); - } - else { - msg.should.have.a.property("payload", "bar"); - } - count++; - if (count === 1) { - done(); - } - } - catch (err) { - done(err); - } - }); - var global = n1.context().global; - global.set("val", "foo", "memory1", function (err) { - global.set("val", "bar", "memory2", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to return things from multiple persistable flow context variables', function (done) { - var flow = [{"id": "n1", "z": "flow", "type": "trigger", - "duration": "20", "wires": [["n2"]], - "op1": "#:(memory1)::val", "op1type": "flow", - "op2": "#:(memory2)::val", "op2type": "flow" - }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - if (count === 0) { - msg.should.have.a.property("payload", "foo"); - } - else { - msg.should.have.a.property("payload", "bar"); - } - count++; - if (count === 1) { - done(); - } - } - catch (err) { - done(err); - } - }); - var flow = n1.context().flow; - flow.set("val", "foo", "memory1", function (err) { - flow.set("val", "bar", "memory2", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to return things from multiple persistable flow & global context variables', function (done) { - var flow = [{"id": "n1", "z": "flow", "type": "trigger", - "duration": "20", "wires": [["n2"]], - "op1": "#:(memory1)::val", "op1type": "flow", - "op2": "#:(memory2)::val", "op2type": "global" - }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - if (count === 0) { - msg.should.have.a.property("payload", "foo"); - } - else { - msg.should.have.a.property("payload", "bar"); - } - count++; - if (count === 1) { - done(); - } - } - catch (err) { - done(err); - } - }); - var context = n1.context(); - var flow = context.flow; - var global = context.flow; - flow.set("val", "foo", "memory1", function (err) { - global.set("val", "bar", "memory2", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to not output anything on first trigger', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"nul", op1:"true",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.a.property("payload", false); - done(); - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should be able to not output anything on second edge', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op2type:"nul", op1:"true",op1type:"val", op2:"false", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.a.property("payload", true); - c += 1; - } - catch(err) { done(err); } - }); - setTimeout( function() { - c.should.equal(1); // should only have had one output. - done(); - },90); - n1.emit("input", {payload:null}); - }); - }); - - it('should be able to reset correctly having not output anything on second edge', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op2type:"nul", op1:"true",op1type:"val", op2:"false", duration:"100", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var errors = []; - n2.on("input", function(msg) { - try { - msg.should.have.a.property("topic", "pass") - msg.should.have.a.property("payload", true); - c += 1; - } - catch(err) { errors.push(err) } - }); - setTimeout( function() { - if (errors.length > 0) { - done(errors[0]) - } else { - c.should.equal(2); - done(); - } - },350); - n1.emit("input", {payload:1, topic:"pass"}); - setTimeout( function() { - n1.emit("input", {payload:2, topic:"should-block"}); - },50); - setTimeout( function() { - n1.emit("input", {payload:3, topic:"pass"}); - },200); - setTimeout( function() { - n1.emit("input", {payload:2, topic:"should-block"}); - },250); - }); - }); - - it('should be able to extend the delay', function(done) { - this.timeout(5000); // add extra time for flake - var spy = sinon.stub(RED.util, 'evaluateNodeProperty').callsFake( - function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } } - ); - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"flow", op1:"foo", op2:"bar", op2type:"global", duration:"100", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "foo"); - c += 1; - } - else { - msg.should.have.a.property("payload", "bar"); - //console.log(Date.now() - ss); - (Date.now() - ss).should.be.greaterThan(149); - spy.restore(); - done(); - } - } - catch(err) { spy.restore(); done(err); } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello"}); - setTimeout( function() { - n1.emit("input", {payload:null}); - },50); - }); - }); - - it('should be able to extend the delay (but with no 2nd output)', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op2type:"nul", op1:"false", op2:"true", duration:"200", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Hello"); - c += 1; - } - else { - msg.should.have.a.property("payload", "World"); - (Date.now() - ss).should.be.greaterThan(300); - done(); - } - } catch(err) { - console.log(err); - done(err); - } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello"}); - setTimeout( function() { - n1.emit("input", {payload:"Error"}); - },50); - setTimeout( function() { - n1.emit("input", {payload:"Error"}); - },100); - setTimeout( function() { - n1.emit("input", {payload:"World"}); - },330); - }); - }); - - it('should be able to extend the delay and output the most recent payload', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"60", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.a.property("payload", "World"); - (Date.now() - ss).should.be.greaterThan(120); - done(); - } - catch(err) { done(err); } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello"}); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye"}); - },40); - setTimeout( function() { - n1.emit("input", {payload:"World"}); - },80); - }); - }); - - it('should be able output the 2nd payload', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"false", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"50", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Goodbye"); - msg.should.have.a.property("topic", "test2"); - c += 1; - } - else { - msg.should.have.a.property("payload", "World"); - msg.should.have.a.property("topic", "test3"); - (Date.now() - ss).should.be.greaterThan(70); - done(); - } - } - catch(err) { done(err); } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello", topic:"test1"}); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye", topic:"test2"}); - },20); - setTimeout( function() { - n1.emit("input", {payload:"World", topic:"test3"}); - },80); - }); - }); - - it('should be able output the 2nd payload and handle multiple topics', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"false", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"80", bytopic:"topic", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Goodbye1"); - msg.should.have.a.property("topic", "test1"); - c += 1; - } - else { - msg.should.have.a.property("payload", "Goodbye2"); - msg.should.have.a.property("topic", "test2"); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"Hello1", topic:"test1"}); - setTimeout( function() { - n1.emit("input", {payload:"Hello2", topic:"test2"}); - },20); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye2", topic:"test2"}); - },20); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye1", topic:"test1"}); - },20); - }); - }); - - it('should be able to apply mustache templates to payloads', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"{{payload}}", op2:"{{topic}}", duration:"50", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Hello"); - c+=1; - } - else { - msg.should.have.a.property("payload", "World"); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"Hello",topic:"World"}); - }); - }); - - it('should be able to send 2nd message to a 2nd output', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"hello", op2:"world", duration:"50", outputs:2, wires:[["n2"],["n3"]] }, - {id:"n2", type:"helper"}, {id:"n3", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "hello"); - msg.should.have.a.property("topic", "test"); - c+=1; - } - else { done(err); } - } - catch(err) { done(err); } - }); - n3.on("input", function(msg) { - try { - if (c === 1) { - msg.should.have.a.property("payload", "world"); - msg.should.have.a.property("topic", "test"); - done(); - } - else { done(err); } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"go",topic:"test"}); - }); - }); - - it('should handle string null as null', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"pay", op1:"null", op2:"null", duration:"40", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", null); - c+=1; - } - else { - msg.should.have.a.property("payload", "World"); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"World"}); - }); - }); - - it('should handle string null as null on op2', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"null", op2:"null", duration:"40", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", null); - c+=1; - } - else { - msg.should.have.a.property("payload", null); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"null"}); - }); - }); - - it('should be able to set infinite timeout, and clear timeout', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"0", extend: false, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },20); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {reset:true}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - }); - }); - - it('should be able to set infinite timeout, and clear timeout by message', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:"foo"}); // don't clear the blockage - n1.emit("input", {payload:"boo"}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },50); - }); - }); - - it('should be able to set infinite timeout, and clear timeout by boolean true', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"true", duration:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },20); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:false}); // don't clear the blockage - n1.emit("input", {payload:true}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - }); - }); - - it('should be able to set infinite timeout, and clear timeout by boolean false', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"false", duration:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },20); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:"foo"}); // don't clear the blockage - n1.emit("input", {payload:false}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - }); - }); - - it('should be able to set a repeat, and clear loop by reset', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", op1:"", op1type:"pay", duration:-25, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c += 1; - try { - msg.should.have.property('payload','foo'); - msg.payload = "bar"; // try to provoke pass by reference error - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"foo"}); // trigger - n1.emit("input", {payload:"foo"}); // trigger - setTimeout( function() { - n1.emit("input", {reset:true}); // reset - },90); - setTimeout( function() { - c.should.within(2,5); // should send foo between 2 and 5 times. - done(); - },180); - }); - }); - describe('messaging API', function () { - function mapiDoneTriggerTestHelper(done, nodeSetting, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - { ...nodeSetting, id: "triggerNode1", type: "trigger", wires: [[]] }, - { id: "completeNode1", type: "complete", scope: ["triggerNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["triggerNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([triggerNode, completeNode, catchNode], flow, function () { - const triggerNode1 = helper.getNode("triggerNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 3; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { triggerNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when first message has been processed', function (done) { - // not when second and more messages are emitted. - mapiDoneTriggerTestHelper(done, { units:"s", duration:"1" }, [ - { msg: { seq: 0, payload: "A"}, delay: 0, avr: 0, var: 100} - ]); - }); - it('should call done() when it receives reset message', function (done) { - mapiDoneTriggerTestHelper(done, {units:"s", duration:"1"}, [ - {msg: { seq: 0, payload: "A", reset:true}, delay: 0, avr: 0, var:100} - ]); - }) - }); -}); diff --git a/test/nodes/core/function/90-exec_spec.js b/test/nodes/core/function/90-exec_spec.js deleted file mode 100644 index c397ea27d..000000000 --- a/test/nodes/core/function/90-exec_spec.js +++ /dev/null @@ -1,973 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var helper = require("node-red-node-test-helper"); -var execNode = require("nr-test-utils").require("@node-red/nodes/core/function/90-exec.js"); -var osType = require("os").type(); - -var child_process = require('child_process'); - -describe('exec node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - helper.unload().then(function() { - helper.stopServer(done); - }); - }); - - it('should be loaded with any defaults', function(done) { - var flow = [{id:"n1", type:"exec", name: "exec1"}]; - helper.load(execNode, flow, function() { - try { - var n1 = helper.getNode("n1"); - n1.should.have.property("name", "exec1"); - n1.should.have.property("cmd", ""); - n1.should.have.property("append", ""); - n1.should.have.property("addpay","payload"); - n1.should.have.property("timer",0); - n1.should.have.property("oldrc","false"); - n1.should.have.property("execOpt"); - n1.execOpt.should.have.property("encoding", 'binary'); - n1.execOpt.should.have.property("maxBuffer", 10000000); - n1.execOpt.should.have.property("windowsHide", false); - n1.should.have.property("spawnOpt"); - n1.spawnOpt.should.have.property("windowsHide", false); - done(); - } catch(err) { - done(err); - } - }); - }); - - describe('calling exec', function() { - - it('should exec a simple command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - // arg3(error,stdout,stderr); - arg3(null,arg1,arg1.toUpperCase()); - }); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received = received + 1; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("echo"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ECHO"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",0); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and"}); - }); - }); - - it('should exec a simple command with appended value from message', function (done) { - var flow = [{id:"n1", type:"exec", wires:[["n2"]], command:"echo", addpay:"topic", append:"more", oldrc:"false"}, - {id:"n2", type:"helper"}]; - helper.load(execNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("bar more\n"); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:"foo", topic:"bar"}); - }); - }); - - it('should exec a simple command with extra parameters', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:"payload", append:"more", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - //console.log(arg1); - // arg3(error,stdout,stderr); - arg3(null,arg1,arg1.toUpperCase()); - }); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - received++; - if (received < 2) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("echo and more"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ECHO AND MORE"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:"and"}); - }); - }); - - it('should be able to return a binary buffer', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:"more", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - //console.log(arg1); - // arg3(error,stdout,stderr); - arg3("error",Buffer.from([0x01,0x02,0x03,0x88]),Buffer.from([0x01,0x02,0x03,0x88])); - }); - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - //console.log("n2",msg); - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.length.should.equal(4); - child_process.exec.restore(); - done(); - } catch(err) { - child_process.exec.restore(); - done(err); - } - }); - n1.receive({}); - }); - }); - - it('should be able to timeout a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - // Although Windows timeout command is equivalent to sleep, this cannot be used because it promptly outputs a message. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"0.3", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"0.3", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - try { - msg.should.have.property("rc"); - msg.rc.should.have.property("code",null); - msg.rc.should.have.property("signal","SIGTERM"); - } catch(err) { done(err); } - }); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:""}); - },150); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command - SIGINT', function(done) { - var flow; - var sig = "SIGINT"; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - try { - msg.should.have.property("rc"); - msg.rc.should.have.property("code",null); - msg.rc.should.have.property("signal","SIGINT"); - } catch(err) { done(err); } - }); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal",sig); - done(); - } catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:"SIGINT"}); - },150); - n1.receive({}); - }); - }); - - it('should return the rc for a failing command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"error", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - //console.log(arg1); - // arg3(error,stdout,stderr); - arg3({code: 1},arg1,arg1.toUpperCase()); - }); - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received++; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("error"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",1); - msg.rc.should.have.property("message",undefined); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ERROR"); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and"}); - }); - }); - - it('should preserve existing properties on msg object', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - // arg3(error,stdout,stderr); - arg3(null,arg1,arg1.toUpperCase()); - }); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received = received + 1; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("echo"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - msg.should.have.property("foo","bar"); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ECHO"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - msg.should.have.property("foo","bar"); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",0); - msg.should.have.property("foo","bar"); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and", foo:"bar"}); - }); - }); - - it('should preserve existing properties on msg object for a failing command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"error", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - // arg3(error,stdout,stderr); - arg3({code: 1},arg1,arg1.toUpperCase()); - }); - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received++; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("error"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",1); - msg.rc.should.have.property("message",undefined); - msg.should.have.property("foo",null); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ERROR"); - msg.should.have.property("foo",null); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - msg.should.have.property("foo",null); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and", foo:null}); - }); - }); - - }); - - describe('calling spawn', function() { - - it('should spawn a simple command', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - // Need to use cmd to spawn a process because Windows echo command is a built-in command and cannot be spawned. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "hello world\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "hello world\n"; - } - var events = require('events'); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var payload = ""; - n2.on("input", function(msg) { - //console.log(msg); - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - payload += msg.payload; - if (payload.endsWith("\n")) { - payload.should.equal(expected); - done(); - } - } - catch(err) { done(err); } - }); - n1.receive({payload:"hello world"}); - }); - }); - - it('should spawn a simple command with a non string payload parameter', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo", addpay:true, append:" deg C", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "12345 deg C\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:" deg C", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "12345 deg C\n"; - } - var payload = ""; - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - //console.log(msg); - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - payload += msg.payload; - if (payload.endsWith("\n")) { - payload.should.equal(expected); - done(); - } - } - catch(err) { done(err); } - }); - n1.receive({payload:12345}); - }); - }); - - it('should spawn a simple command and return binary buffer', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - if (osType === "Windows_NT") { - msg.payload.length.should.equalOneOf(6,8); - } else { - msg.payload.length.should.equal(7); - } - done(); - } - catch(err) { done(err); } - }); - n1.receive({payload:Buffer.from([0x01,0x02,0x03,0x88])}); - }); - }); - - it('should work if passed multiple words to spawn command', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\n"; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - received++; - if (received < 2) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal(expected); - - msg = messages[1]; - msg.should.have.property("payload"); - should.exist(msg.payload); - msg.payload.should.have.property("code",0); - done(); - } - catch(err) { - done(err); - } - }; - - n2.on("input", function(msg) { - var payload = msg.payload; - if (messages[0]) { - messages[0].payload += payload; - } - else { - messages[0] = msg; - } - if (payload.endsWith("\n")) { - completeTest(); - } - }); - n4.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:null,fred:123}); - }); - }); - - it('should return an error for a bad command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"madeupcommandshouldfail", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("code"); - msg.payload.code.should.be.below(0); - done(); - } - catch(err) { done(err); } - }); - n1.receive({payload:null}); - }); - }); - - it('should return an error for a failing command', function(done) { - var flow; - var expected; - var expectedFound = false; - if (osType === "Windows_NT") { - // Cannot use mkdir because Windows mkdir command automatically creates non-existent directories. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "IP address must be specified."; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"mkdir /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = ' directory'; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n3.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - if (msg.payload.indexOf(expected) >= 0) { - // The error text on the stderr stream might get sent in more than one piece. - // We only need to know that it occurred before the return code is sent, - // as checked below in node n4. - expectedFound = true; - } - } - catch(err) { done(err); } - }); - n4.on("input", function(msg) { - try { - expectedFound.should.be.true; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - done(); - } - catch(err) { done(err); } - }); - n1.receive({payload:null}); - }); - }); - - it('should be able to timeout a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000", timer:"0.3", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"0.3", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("code",null); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:""}); - },150); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command - SIGINT', function(done) { - var flow; - var sig = "SIGINT"; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal",sig); - done(); - } - catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:"SIGINT"}); - },150); - n1.receive({}); - }); - }); - - it('should preserve existing properties on msg object', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\n"; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - received++; - if (received < 2) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal(expected); - msg.should.have.property("foo",123); - - msg = messages[1]; - msg.should.have.property("payload"); - should.exist(msg.payload); - msg.payload.should.have.property("code",0); - msg.should.have.property("foo",123); - - done(); - } - catch(err) { - done(err); - } - }; - - n2.on("input", function(msg) { - var payload = msg.payload; - if (messages[0]) { - messages[0].payload += payload; - } - else { - messages[0] = msg; - } - if (payload.endsWith("\n")) { - completeTest(); - } - }); - n4.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:null,foo:123}); - }); - }); - - it('should preserve existing properties on msg object for a failing command', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - // Cannot use mkdir because Windows mkdir command automatically creates non-existent directories. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "IP address must be specified."; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"mkdir /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = ' directory'; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - if (messages[0] === null || messages[1] === null) { - // We have not yet had responses on both ports. - return - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.should.have.property("foo","baz"); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - msg.should.have.property("foo","baz"); - - done(); - } - catch(err) { - done(err); - } - }; - - n3.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:null,foo:"baz"}); - }); - }); - - }); -}); diff --git a/test/nodes/core/function/rbe_spec.js b/test/nodes/core/function/rbe_spec.js deleted file mode 100644 index 2ef005a27..000000000 --- a/test/nodes/core/function/rbe_spec.js +++ /dev/null @@ -1,538 +0,0 @@ - -var should = require("should"); -var helper = require("node-red-node-test-helper"); - -var testNode = require("nr-test-utils").require("@node-red/nodes/core/function/rbe.js"); - -describe('rbe node', function() { - "use strict"; - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - helper.unload().then(function() { - helper.stopServer(done); - }); - }); - - it("should be loaded with correct defaults", function(done) { - var flow = [{"id":"n1", "type":"rbe", "name":"rbe1", "wires":[[]]}]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property("name", "rbe1"); - n1.should.have.property("func", "rbe"); - n1.should.have.property("gap", "0"); - done(); - }); - }); - - it('should only send output if payload changes - with multiple topics (rbe)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", "a"); - c+=1; - } - else if (c === 1) { - msg.should.have.a.property("payload", 2); - c+=1; - } - else if (c == 2) { - msg.should.have.a.property("payload"); - msg.payload.should.have.a.property("b",1); - msg.payload.should.have.a.property("c",2); - c+=1; - } - else if (c == 3) { - msg.should.have.a.property("payload",true); - c+=1; - } - else if (c == 4) { - msg.should.have.a.property("payload",false); - c+=1; - } - else if (c == 5) { - msg.should.have.a.property("payload",true); - c+=1; - } - else if (c == 6) { - msg.should.have.a.property("topic","a"); - msg.should.have.a.property("payload",1); - c+=1; - } - else if (c == 7) { - msg.should.have.a.property("topic","b"); - msg.should.have.a.property("payload",1); - c+=1; - } - else { - c += 1; - msg.should.have.a.property("topic","c"); - msg.should.have.a.property("payload",1); - done(); - } - }); - n1.emit("input", {payload:"a"}); - n1.emit("input", {payload:"a"}); - n1.emit("input", {payload:"a"}); - n1.emit("input", {payload:2}); - n1.emit("input", {payload:2}); - n1.emit("input", {payload:{b:1,c:2}}); - n1.emit("input", {payload:{c:2,b:1}}); - n1.emit("input", {payload:{c:2,b:1}}); - n1.emit("input", {payload:true}); - n1.emit("input", {payload:false}); - n1.emit("input", {payload:false}); - n1.emit("input", {payload:true}); - - n1.emit("input", {topic:"a",payload:1}); - n1.emit("input", {topic:"b",payload:1}); - n1.emit("input", {topic:"b",payload:1}); - n1.emit("input", {topic:"a",payload:1}); - n1.emit("input", {topic:"c",payload:1}); - - }); - }); - - it('should ignore multiple topics if told to (rbe)', function(done) { - var flow = [{id:"n1", type:"rbe", func:"rbe", gap:"0", septopics:false, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", "a"); - c+=1; - } - else if (c === 1) { - msg.should.have.a.property("payload", 2); - c+=1; - } - else if (c == 2) { - msg.should.have.a.property("payload"); - msg.payload.should.have.a.property("b",1); - msg.payload.should.have.a.property("c",2); - c+=1; - } - else if (c == 3) { - msg.should.have.a.property("payload",true); - c+=1; - } - else if (c == 4) { - msg.should.have.a.property("payload",false); - c+=1; - } - else if (c == 5) { - msg.should.have.a.property("payload",true); - c+=1; - } - else if (c == 6) { - msg.should.have.a.property("topic","a"); - msg.should.have.a.property("payload",1); - c+=1; - } - else { - msg.should.have.a.property("topic","a"); - msg.should.have.a.property("payload",2); - done(); - } - }); - n1.emit("input", {topic:"a",payload:"a"}); - n1.emit("input", {topic:"b",payload:"a"}); - n1.emit("input", {topic:"c",payload:"a"}); - n1.emit("input", {topic:"a",payload:2}); - n1.emit("input", {topic:"b",payload:2}); - n1.emit("input", {payload:{b:1,c:2}}); - n1.emit("input", {payload:{c:2,b:1}}); - n1.emit("input", {payload:{c:2,b:1}}); - n1.emit("input", {topic:"a",payload:true}); - n1.emit("input", {topic:"b",payload:false}); - n1.emit("input", {topic:"c",payload:false}); - n1.emit("input", {topic:"d",payload:true}); - - n1.emit("input", {topic:"a",payload:1}); - n1.emit("input", {topic:"b",payload:1}); - n1.emit("input", {topic:"c",payload:1}); - n1.emit("input", {topic:"d",payload:1}); - n1.emit("input", {topic:"a",payload:2}); - - }); - }); - - it('should only send output if another chosen property changes - foo (rbe)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", property:"foo", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("foo", "a"); - c+=1; - } - else if (c === 1) { - msg.should.have.a.property("foo", "b"); - c+=1; - } - else { - msg.should.have.a.property("foo"); - msg.foo.should.have.a.property("b",1); - msg.foo.should.have.a.property("c",2); - done(); - } - }); - n1.emit("input", {foo:"a"}); - n1.emit("input", {payload:"a"}); - n1.emit("input", {foo:"a"}); - n1.emit("input", {payload:"a"}); - n1.emit("input", {foo:"a"}); - n1.emit("input", {foo:"b"}); - n1.emit("input", {foo:{b:1,c:2}}); - n1.emit("input", {foo:{c:2,b:1}}); - n1.emit("input", {payload:{c:2,b:1}}); - }); - }); - - it('should only send output if payload changes - ignoring first value (rbei)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"rbei", gap:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", "b"); - msg.should.have.a.property("topic", "a"); - c+=1; - } - else if (c === 1) { - msg.should.have.a.property("payload", "b"); - msg.should.have.a.property("topic", "b"); - c+=1; - } - else if (c === 2) { - msg.should.have.a.property("payload", "c"); - msg.should.have.a.property("topic", "a"); - c+=1; - } - else { - msg.should.have.a.property("payload", "c"); - msg.should.have.a.property("topic", "b"); - done(); - } - - }); - n1.emit("input", {payload:"a", topic:"a"}); - n1.emit("input", {payload:"a", topic:"b"}); - n1.emit("input", {payload:"a", topic:"a"}); - n1.emit("input", {payload:"b", topic:"a"}); - n1.emit("input", {payload:"b", topic:"b"}); - n1.emit("input", {payload:"c", topic:"a"}); - n1.emit("input", {payload:"c", topic:"b"}); - }); - }); - - it('should send output if queue is reset (rbe)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", "a"); - c+=1; - } - else if (c === 1) { - msg.should.have.a.property("payload", "b"); - c+=1; - } - else if (c === 2) { - msg.should.have.a.property("payload", "a"); - c+=1; - } - else if (c === 3) { - msg.should.have.a.property("payload", "b"); - c+=1; - } - else if (c === 4) { - msg.should.have.a.property("payload", "b"); - c+=1; - } - else if (c === 5) { - msg.should.have.a.property("payload", "b"); - c+=1; - } - else if (c === 6) { - msg.should.have.a.property("payload", "a"); - c+=1; - } - else { - msg.should.have.a.property("payload", "c"); - done(); - } - }); - n1.emit("input", {topic:"a", payload:"a"}); - n1.emit("input", {topic:"a", payload:"a"}); - n1.emit("input", {topic:"b", payload:"b"}); - n1.emit("input", {reset:true}); // reset all - n1.emit("input", {topic:"a", payload:"a"}); - n1.emit("input", {topic:"b", payload:"b"}); - n1.emit("input", {topic:"b", payload:"b"}); - n1.emit("input", {topic:"b", reset:""}); // reset b - n1.emit("input", {topic:"b", payload:"b"}); - n1.emit("input", {topic:"a", payload:"a"}); - n1.emit("input", {reset:""}); // reset all - n1.emit("input", {topic:"b", payload:"b"}); - n1.emit("input", {topic:"a", payload:"a"}); - n1.emit("input", {topic:"c"}); // don't reset a non topic - n1.emit("input", {topic:"b", payload:"b"}); - n1.emit("input", {topic:"a", payload:"a"}); - n1.emit("input", {topic:"c", payload:"c"}); - }); - }); - - it('should only send output if x away from original value (deadbandEq)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"deadbandEq", gap:"10", inout:"out", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c = c + 1; - if (c === 1) { - msg.should.have.a.property("payload", 0); - } - else if (c === 2) { - msg.should.have.a.property("payload", 10); - } - else if (c == 3) { - msg.should.have.a.property("payload", 20); - done(); - } - }); - n1.emit("input", {payload:0}); - n1.emit("input", {payload:2}); - n1.emit("input", {payload:4}); - n1.emit("input", {payload:6}); - n1.emit("input", {payload:8}); - n1.emit("input", {payload:10}); - n1.emit("input", {payload:15}); - n1.emit("input", {payload:20}); - }); - }); - - it('should only send output if more than x away from original value (deadband)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c = c + 1; - //console.log(c,msg); - if (c === 1) { - msg.should.have.a.property("payload", 0); - } - else if (c === 2) { - msg.should.have.a.property("payload", 20); - } - else { - msg.should.have.a.property("payload", "5 deg"); - done(); - } - }); - n1.emit("input", {payload:0}); - n1.emit("input", {payload:2}); - n1.emit("input", {payload:4}); - n1.emit("input", {payload:"6 deg"}); - n1.emit("input", {payload:8}); - n1.emit("input", {payload:20}); - n1.emit("input", {payload:15}); - n1.emit("input", {payload:"5 deg"}); - }); - }); - - it('should only send output if more than x% away from original value (deadband)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10%", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c = c + 1; - if (c === 1) { - msg.should.have.a.property("payload", 100); - } - else if (c === 2) { - msg.should.have.a.property("payload", 111); - } - else if (c === 3) { - msg.should.have.a.property("payload", 135); - done(); - } - }); - n1.emit("input", {payload:100}); - n1.emit("input", {payload:95}); - n1.emit("input", {payload:105}); - n1.emit("input", {payload:111}); - n1.emit("input", {payload:120}); - n1.emit("input", {payload:135}); - }); - }); - - it('should warn if no number found in deadband mode', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c += 1; - }); - setTimeout( function() { - c.should.equal(0); - helper.log().called.should.be.true; - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "rbe"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().WARN); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'rbe'); - msg.should.have.property('msg', 'rbe.warn.nonumber'); - done(); - },50); - n1.emit("input", {payload:"banana"}); - }); - }); - - it('should not send output if x away or greater from original value (narrowbandEq)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"narrowbandEq", gap:"10", inout:"out", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c = c + 1; - //console.log(c,msg); - if (c === 1) { - msg.should.have.a.property("payload", 0); - } - else if (c === 2) { - msg.should.have.a.property("payload", 5); - } - else if (c === 3) { - msg.should.have.a.property("payload", 10); - done(); - } - }); - n1.emit("input", {payload:0}); - n1.emit("input", {payload:10}); - n1.emit("input", {payload:5}); - n1.emit("input", {payload:15}); - n1.emit("input", {payload:10}); - n1.emit("input", {payload:20}); - n1.emit("input", {payload:25}); - }); - }); - - it('should not send output if more than x away from original value (narrowband)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"10", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", 0); - } - else if (c === 1) { - msg.should.have.a.property("payload","6 deg"); - } - else { - msg.should.have.a.property("payload", "5 deg"); - done(); - } - c += 1; - }); - n1.emit("input", {payload:0}); - n1.emit("input", {payload:20}); - n1.emit("input", {payload:40}); - n1.emit("input", {payload:"6 deg"}); - n1.emit("input", {payload:18}); - n1.emit("input", {payload:20}); - n1.emit("input", {payload:50}); - n1.emit("input", {payload:"5 deg"}); - }); - }); - - it('should send output if gap is 0 and input doesnt change (narrowband)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", 1); - } - else if (c === 4) { - msg.should.have.a.property("payload",1); - done(); - } - c += 1; - }); - n1.emit("input", {payload:1}); - n1.emit("input", {payload:1}); - n1.emit("input", {payload:1}); - n1.emit("input", {payload:1}); - n1.emit("input", {payload:0}); - n1.emit("input", {payload:1}); - }); - }); - - it('should not send output if more than x away from original value (narrowband in step mode)', function(done) { - var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"10", inout:"in", start:"500", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.a.property("payload", 55); - } - else if (c === 1) { - msg.should.have.a.property("payload", 205); - done(); - } - c += 1; - }); - n1.emit("input", {payload:50}); - n1.emit("input", {payload:55}); - n1.emit("input", {payload:200}); - n1.emit("input", {payload:205}); - }); - }); -}); diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js deleted file mode 100644 index 07bed5a01..000000000 --- a/test/nodes/core/network/21-httprequest_spec.js +++ /dev/null @@ -1,2221 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var http = require("http"); -var https = require("https"); -var should = require("should"); -var express = require("express"); -var bodyParser = require('body-parser'); -var stoppable = require('stoppable'); -var helper = require("node-red-node-test-helper"); -var httpRequestNode = require("nr-test-utils").require("@node-red/nodes/core/network/21-httprequest.js"); -var tlsNode = require("nr-test-utils").require("@node-red/nodes/core/network/05-tls.js"); -var httpProxyNode = require("nr-test-utils").require("@node-red/nodes/core/network/06-httpproxy.js"); -var hashSum = require("hash-sum"); -var httpProxy = require('proxy'); -var cookieParser = require('cookie-parser'); -var multer = require("multer"); -var RED = require("nr-test-utils").require("node-red/lib/red"); -var fs = require('fs-extra'); -var auth = require('basic-auth'); - -describe('HTTP Request Node', function() { - var testApp; - var testServer; - var testPort = 10234; - var testSslServer; - var testSslPort = 10334; - var testProxyServer; - var testProxyPort = 10444; - var testProxyServerAuth; - var testProxyAuthPort = 10554; - var testSslClientServer; - var testSslClientPort = 10664; - - //save environment variables - var preEnvHttpProxyLowerCase; - var preEnvHttpProxyUpperCase; - var preEnvNoProxyLowerCase; - var preEnvNoProxyUpperCase; - - //rediect cookie variables - var receivedCookies = {}; - - function startServer(done) { - testPort += 1; - testServer = stoppable(http.createServer(testApp)); - testServer.listen(testPort,function(err) { - testSslPort += 1; - console.log("ssl port", testSslPort); - var sslOptions = { - key: fs.readFileSync('test/resources/ssl/server.key'), - cert: fs.readFileSync('test/resources/ssl/server.crt') - /* - Country Name (2 letter code) [AU]: - State or Province Name (full name) [Some-State]: - Locality Name (eg, city) []: - Organization Name (eg, company) [Internet Widgits Pty Ltd]: - Organizational Unit Name (eg, section) []: - Common Name (e.g. server FQDN or YOUR name) []:localhost - Email Address []: - - Please enter the following 'extra' attributes to be sent with your certificate request - A challenge password []: - An optional company name []: - */ - }; - testSslServer = stoppable(https.createServer(sslOptions,testApp)); - testSslServer.listen(testSslPort, function(err){ - if (err) { - console.log(err); - } else { - console.log("started testSslServer"); - } - }); - - testSslClientPort += 1; - var sslClientOptions = { - key: fs.readFileSync('test/resources/ssl/server.key'), - cert: fs.readFileSync('test/resources/ssl/server.crt'), - ca: fs.readFileSync('test/resources/ssl/server.crt'), - requestCert: true - }; - testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp)); - testSslClientServer.listen(testSslClientPort, function(err){ - console.log("ssl-client", err) - }); - - testProxyPort += 1; - testProxyServer = stoppable(httpProxy(http.createServer())) - - testProxyServer.on('request', function(req,res){ - if (!res.headersSent) { - res.setHeader("x-testproxy-header", "foobar") - } - }) - testProxyServer.listen(testProxyPort) - - testProxyAuthPort += 1 - testProxyServerAuth = stoppable(httpProxy(http.createServer())) - testProxyServerAuth.authenticate = function(req,callback){ - var authHeader = req.headers['proxy-authorization']; - if (authHeader) { - var user = auth.parse(authHeader) - if (user.name == "foouser" && user.pass == "barpassword") { - callback(null, true) - } else { - callback(null, false) - } - } else { - callback(null, false) - } - } - testProxyServerAuth.on('request', function(req,res){ - if (!res.headersSent) { - res.setHeader("x-testproxy-header", "foobar") - } - }) - testProxyServerAuth.listen(testProxyAuthPort) - - done(err); - }); - } - - function getTestURL(url) { - return "http://localhost:"+testPort+url; - } - - function getSslTestURL(url) { - return "https://localhost:"+testSslPort+url; - } - - function getSslClientTestURL(url) { - return "https://localhost:"+testSslClientPort+url; - } - - function getDifferentTestURL(url) { - return "http://127.0.0.1:"+testPort+url; - } - - function getSslTestURLWithoutProtocol(url) { - return "localhost:"+testSslPort+url; - } - - function deleteProxySetting() { - delete process.env.http_proxy; - delete process.env.HTTP_PROXY; - delete process.env.no_proxy; - delete process.env.NO_PROXY; - } - - before(function(done) { - - testApp = express(); - - // The fileupload test needs a different set of middleware - so mount - // as a separate express instance - var fileUploadApp = express(); - var mp = multer({ storage: multer.memoryStorage() }).any(); - fileUploadApp.post("/file-upload",function(req,res,next) { - mp(req,res,function(err) { - req._body = true; - next(err); - }) - },bodyParser.json(),function(req,res) { - res.json({ - body: req.body, - files: req.files - }) - }); - testApp.use(fileUploadApp); - - testApp.use(bodyParser.raw({type:"*/*"})); - testApp.use(cookieParser(undefined,{decode:String})); - testApp.get('/statusCode204', function(req,res) { res.status(204).end();}); - testApp.get('/text', function(req, res){ res.send('hello'); }); - testApp.get('/redirectToText', function(req, res){ res.status(302).set('Location', getTestURL('/text')).end(); }); - testApp.get('/json-valid', function(req, res){ res.json({a:1}); }); - testApp.get('/json-invalid', function(req, res){ res.set('Content-Type', 'application/json').send("{a:1"); }); - testApp.get('/headersInspect', function(req, res){ res.set('x-test-header', 'bar').send("a"); }); - testApp.get('/timeout', function(req, res){ - setTimeout(function() { - res.send('hello'); - }, 10000); - }); - testApp.get('/timeout50ms', function(req, res){ - setTimeout(function() { - res.send('hello'); - }, 50); - }); - testApp.get('/checkCookie', function(req, res){ - res.send(req.cookies); - }); - testApp.get('/setCookie', function(req, res){ - res.cookie('data','hello'); - res.send(""); - }); - testApp.get('/authenticate', function(req, res){ - let result; - let authHeader = req.headers['authorization']; - if (/^Basic/.test(authHeader)) { - result = auth.parse(authHeader); - result.user = result.name; - } else if (/^Bearer/.test(authHeader)) { - result = { - token: authHeader.substring(7) - } - } - res.json(result); - }); - testApp.get('/proxyAuthenticate', function(req, res){ - // var user = auth.parse(req.headers['proxy-authorization']); - var result = { - //user: user.name, - //pass: user.pass, - headers: req.headers - }; - res.json(result); - }); - testApp.post('/postInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.put('/putInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.delete('/deleteInspect', function(req,res) { res.status(204).end();}); - testApp.head('/headInspect', function(req,res) { res.status(204).end();}); - testApp.patch('/patchInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.trace('/traceInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.options('/*', function(req,res) { - res.status(200).end(); - }); - testApp.get('/redirectToSameDomain', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectToSameDomainCookie','same1'); - res.redirect(getTestURL('/redirectReturn')); - }); - testApp.get('/redirectToDifferentDomain', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectToDifferentDomain','different1'); - res.redirect(getDifferentTestURL('/redirectReturn')); - }); - testApp.get('/redirectMultipleTimes', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectMultipleTimes','multiple1'); - res.redirect(getTestURL('/redirectToDifferentDomain')); - }); - testApp.get('/redirectReturn', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectReturn','return1'); - res.status(200).end(); - }); - testApp.get('/getQueryParams', function(req,res) { - res.json({ - query:req.query, - url: req.originalUrl - }); - }) - testApp.get('/returnError/:code', function(req,res) { - res.status(parseInt(req.params.code)).json({gotError:req.params.code}); - }) - - testApp.get('/rawHeaders', function(req,res) { - const result = {}; - for (let i=0;i { - testProxyServer.stop(() => { - testProxyServerAuth.stop(() => { - testSslServer.stop(() => { - testSslClientServer.stop(() => { - helper.stopServer(done); - }) - }); - }); - }); - }); - }); - - beforeEach(function() { - preEnvHttpProxyLowerCase = process.env.http_proxy; - preEnvHttpProxyUpperCase = process.env.HTTP_PROXY; - preEnvNoProxyLowerCase = process.env.no_proxy; - preEnvNoProxyUpperCase = process.env.NO_PROXY; - process.env.no_proxy = 'localhost'; - process.env.NO_PROXY = 'localhost'; - }); - - afterEach(function() { - process.env.http_proxy = preEnvHttpProxyLowerCase; - process.env.HTTP_PROXY = preEnvHttpProxyUpperCase; - // On Windows, if environment variable of NO_PROXY that includes lower cases - // such as No_Proxy is replaced with NO_PROXY. - process.env.no_proxy = preEnvNoProxyLowerCase; - process.env.NO_PROXY = preEnvNoProxyUpperCase; - if (preEnvHttpProxyLowerCase == undefined) { - delete process.env.http_proxy; - } - if (preEnvHttpProxyUpperCase == undefined) { - delete process.env.HTTP_PROXY; - } - if (preEnvNoProxyLowerCase == undefined) { - delete process.env.no_proxy; - } - if (preEnvNoProxyUpperCase == undefined) { - delete process.env.NO_PROXY; - } - helper.unload(); - }); - - describe('request', function() { - it('should get plain text content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.redirectList.length.should.equal(0); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should get JSON content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-valid')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{a:1}); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should send the payload as the body of a POST as application/json', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('{"foo":"abcde"}'); - msg.payload.headers.should.have.property('content-type').which.startWith('application/json'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}}); - }); - }); - - it('should send a payload of 0 as the body of a POST as text/plain', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('0'); - msg.payload.headers.should.have.property('content-length','1'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:0, headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send an Object payload as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('{"foo":"abcde"}'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}, headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send a Buffer as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('hello'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:Buffer.from('hello'), headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send form-based request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.body.should.equal("foo=1%202%203&bar="); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.have.property('content-type','application/x-www-form-urlencoded'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:'1 2 3', bar:''}, headers: { 'content-type': 'application/x-www-form-urlencoded'}}); - }); - }); - - it('should send PUT request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"PUT",ret:"obj",url:getTestURL('/putInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('foo'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send DELETE request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"DELETE",ret:"obj",url:getTestURL('/deleteInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}}); - }); - }); - - it('should send HEAD request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",url:getTestURL('/headInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"head"}); - }); - }); - - it('should send PATCH request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"PATCH",ret:"obj",url:getTestURL('/patchInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('foo'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('etag'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send OPTIONS request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/*')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"options"}); - }); - }); - - it('should send TRACE request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/traceInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('foo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"trace", headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should get Buffer content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"bin",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should return plain text when JSON fails to parse', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-invalid')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',"{a:1"); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should return the status code', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/statusCode204')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use msg.url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", url:"/foo"}); - }); - }); - - it('should output an error when URL is not provided', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:""}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo"}); - setTimeout(function() { - if (inError) { - done(new Error("no url allowed though")); - } else { - done(); - } - },20); - }); - }); - - it('should allow the message to provide the url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",url:getTestURL('/text')}); - }); - }); - - it('should allow the url to contain mustache placeholders', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/te{{placeholder}}')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",placeholder:"xt"}); - }); - }); - - it('should allow the url to be missing the http:// prefix', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text').substring("http://".length)}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should reject non http:// schemes - node config', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:"ftp://foo"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo"}); - setTimeout(function() { - if (inError) { - done(new Error("non http(s):// scheme allowed through")); - } else { - done(); - } - },20); - }); - }); - - it('should reject non http:// schemes - msg.url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo",url:"ftp://foo"}); - setTimeout(function() { - if (inError) { - done(new Error("non http(s):// scheme allowed through")); - } else { - done(); - } - },20); - }); - }); - - it('should use msg.method', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"POST"}); - }); - }); - - it('should allow the message to provide the method', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",method:"get"}); - }); - }); - - it('should receive msg.responseUrl', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.should.have.property('responseUrl', getTestURL('/text')); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should receive msg.responseUrl when redirected', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/redirectToText')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('responseUrl', getTestURL('/text')); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should prevent following redirect when msg.followRedirects is false', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/redirectToText')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',302); - msg.should.have.property('responseUrl', getTestURL('/redirectToText')); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",followRedirects:false}); - }); - }); - - it('should output an error when request timeout occurred', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')}, - {id:"n2", type:"helper"}]; - var timeout = RED.settings.httpRequestTimeout; - RED.settings.httpRequestTimeout = 50; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode'); - /TIMEDOUT/.test(msg.statusCode).should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().ERROR, id:'n1',type:'http request',msg:'common.notification.errors.no-response', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } finally { - RED.settings.httpRequestTimeout = timeout; - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should output an error when request timeout occurred when set via msg.requestTimeout', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode'); - /TIMEDOUT/.test(msg.statusCode).should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().ERROR, id:'n1',type:'http request',msg:'common.notification.errors.no-response', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: 50}); - }); - }); - it('should show a warning if msg.requestTimeout is not a number', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode', 200); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(2); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnan', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: "foo"}); - }); - }); - it('should show a warning if msg.requestTimeout is negative', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode', 200); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(2); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnegative', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: -4}); - }); - }); - it('should show a warning if msg.requestTimeout is set to 0', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode', 200); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(2); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnegative', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: 0}); - }); - }); - it('should pass if response time is faster than timeout set via msg.requestTimeout', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout50ms')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: 100}); - }); - }); - it('should append query params to url - obj', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",paytoqs:true,ret:"obj",url:getTestURL('/getQueryParams')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ - query:{a:'1',b:'2',c:'3'}, - url: '/getQueryParams?a=1&b=2&c=3' - }); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{a:1,b:2,c:3}}); - }); - }); - - it('should send a msg for non-2xx response status - 400', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/400')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ gotError: '400' }); - msg.should.have.property('statusCode',400); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({}); - }) - }); - it('should send a msg for non-2xx response status - 404', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/404')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ gotError: '404' }); - msg.should.have.property('statusCode',404); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({}); - }) - }); - it('should send a msg for non-2xx response status - 500', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/500')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ gotError: '500' }); - msg.should.have.property('statusCode',500); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({}); - }) - }); - - it('should encode the url to handle special characters', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ - query:{ a: 'b', c:[ 'T24,0°|H80%|W S8,3m/s' ] }, - url: '/getQueryParams?a=b&c[0].Text=T24,0%C2%B0|H80%25|W%20S8,3m/s' - }); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({url: getTestURL('/getQueryParams')+"?a=b&c[0].Text=T24,0°|H80%|W%20S8,3m/s"}); - }); - }) - }); - - describe('HTTP header', function() { - it('should receive cookie', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/setCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.responseCookies.should.have.property('data'); - msg.responseCookies.data.should.have.property('value','hello'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should send cookie with string', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:'abc'}}); - }); - }); - - it('should send multiple cookies with string', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.payload.should.have.property('foo','bar'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:'abc',foo:'bar'}}); - }); - }); - - it('should send cookie with object data', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:'abc'}}}); - }); - }); - - it('should send multiple cookies with object data', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.payload.should.have.property('foo','bar'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:'abc'},foo:{value:'bar'}}}); - }); - }); - - it('should encode cookie value', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var value = ';,/?:@ &=+$#'; - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data',encodeURIComponent(value)); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:value}}); - }); - }); - - it('should encode cookie object', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var value = ';,/?:@ &=+$#'; - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data',encodeURIComponent(value)); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:value, encode:true}}}); - }); - }); - - it('should not encode cookie when encode option is false', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var value = '!#$%&\'()*+-./:<>?@[]^_`{|}~'; - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data',value); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:value, encode:false}}}); - }); - }); - - it('should send cookie by msg.headers', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{boo:'123'}, headers:{'cookie':'data=abc'}}); - }); - }); - - it('should send multiple cookies by msg.headers', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.payload.should.have.property('foo','bar'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{boo:'123'}, headers:{'cookie':'data=abc; foo=bar;'}}); - }); - }); - - it('should convert all HTTP headers into lower case', function(done) { - // This is a bad test. Express lower-cases headers in the `req.headers` object, - // so this is actually testing express, not the original request. - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.payload.headers.should.have.property('content-length', "3"); - msg.payload.headers.should.have.property('if-modified-since','Sun, 01 Jun 2000 00:00:00 GMT'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'Content-Type':'text/plain', 'Content-Length': "3", 'If-Modified-Since':'Sun, 01 Jun 2000 00:00:00 GMT'}}); - }); - }); - - it('should keep HTTP header case as provided by the user', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/rawHeaders')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.have.property('Content-Type').which.startWith('text/plain'); - msg.payload.headers.should.have.property('X-Test-HEAD', "foo"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'Content-Type':'text/plain', "X-Test-HEAD": "foo"}}); - }); - }); - it('should receive HTTP header', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/headersInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.headers.should.have.property('x-test-header','bar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should ignore unmodified x-node-red-request-node header', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.headers.should.have.property('content-type').which.startWith('application/json'); - msg.payload.headers.should.not.have.property('x-node-red-request-node'); - done(); - } catch(err) { - done(err); - } - }); - // Pass in a headers property with an unmodified x-node-red-request-node hash - // This should cause the node to ignore the headers - - var headers = { 'content-type': 'text/plain' }; - headers['x-node-red-request-node'] = require("hash-sum")(headers); - - n1.receive({payload:{foo:"bar"}, headers: headers}); - }); - }); - - it('should use modified msg.headers property', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.payload.headers.should.not.have.property('x-node-red-request-node'); - done(); - } catch(err) { - done(err); - } - }); - // Pass in a headers property with a x-node-red-request-node hash that doesn't match the contents - // This should cause the node to use the headers - n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}}); - }); - }); - }); - - describe('protocol', function() { - it('should use msg.rejectUnauthorized', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", rejectUnauthorized: false}); - }); - }); - - it('should use tls-config', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURLWithoutProtocol('/text'),tls:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"", verifyservercert:false}]; - var testNodes = [httpRequestNode, tlsNode]; - helper.load(testNodes, flow, function() { - var n3 = helper.getNode("n3"); - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use tls-config and verify serverCert', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text'),tls:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:true}]; - var testNodes = [httpRequestNode, tlsNode]; - helper.load(testNodes, flow, function() { - var n3 = helper.getNode("n3"); - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use tls-config and send client cert', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslClientTestURL('/getClientCert'),tls:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:false}]; - var testNodes = [httpRequestNode,tlsNode]; - helper.load(testNodes, flow, function() { - var n3 = helper.getNode("n3"); - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }) - }); - - //Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy - /* */ - it('should use http_proxy', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://localhost:" + testProxyPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - /* */ - - it('should use http_proxy when environment variable is invalid', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "invalidvalue"; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - // Remove HTTP-Proxy Authentication tests - /* */ - it('should use HTTP_PROXY', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.HTTP_PROXY = "http://localhost:" + testProxyPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* */ - - it('should use no_proxy', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://localhost:" + testProxyPort; - process.env.no_proxy = "foo,localhost"; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use NO_PROXY', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.HTTP_PROXY = "http://localhost:" + testProxyPort; - process.env.NO_PROXY = "foo,localhost"; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - // Remove HTTP-Proxy Authentication tests - /* */ - it('should use http-proxy-config', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect'),proxy:"n3"}, - {id:"n2",type:"helper"}, - {id:"n3",type:"http proxy",url:"http://localhost:" + testProxyPort} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* */ - - it('should not use http-proxy-config when invalid url is specified', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"invalidvalue"} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use http-proxy-config when valid noproxy is specified', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"http://localhost:" + testProxyPort,noproxy:["foo","localhost"]} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - }); - describe('authentication', function() { - - it('should authenticate on server - basic', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/authenticate')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.credentials = {user:'userfoo', password:'passwordfoo'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('user', 'userfoo'); - msg.payload.should.have.property('pass', 'passwordfoo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - it('should authenticate on server - basic', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/authenticate')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.credentials = {user:'foo@example.com', password:'passwordfoo'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('user', 'foo@example.com'); - msg.payload.should.have.property('pass', 'passwordfoo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - it('should authenticate on server - bearer', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"bearer", url:getTestURL('/authenticate')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.credentials = {password:'passwordfoo'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('token', 'passwordfoo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - // Removed the Proxy Tests until a new mock proxy can be replaced with - // one that supports HTTP Connect verb - /* */ - it('should authenticate on proxy server', function(done) { - var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://foouser:barpassword@localhost:" + testProxyAuthPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - //msg.payload.should.have.property('user', 'foouser'); - //msg.payload.should.have.property('pass', 'barpassword'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* - it('should output an error when proxy authentication was failed', function(done) { - var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://xxxuser:barpassword@localhost:" + testProxyAuthPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',407); - msg.headers.should.have.property('proxy-authenticate', 'BASIC realm="proxy"'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - */ - it('should authenticate on proxy server(http-proxy-config)', function(done) { - var flow = [ - {id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"http://localhost:" + testProxyAuthPort} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - n3.credentials = {username:'foouser', password:'barpassword'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - // msg.payload.should.have.property('user', 'foouser'); - // msg.payload.should.have.property('pass', 'barpassword'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* - it('should output an error when proxy authentication was failed(http-proxy-config)', function(done) { - var flow = [ - {id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"http://@localhost:" + testProxyAuthPort} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - n3.credentials = {username:'xxxuser', password:'barpassword'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',407); - msg.headers.should.have.property('proxy-authenticate', 'BASIC realm="proxy"'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - */ - - }); - - describe('file-upload', function() { - it('should upload a file', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'POST',ret:'obj',url:getTestURL('/file-upload')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property("body",{"other":"123"}); - msg.payload.should.have.property("files"); - msg.payload.files.should.have.length(1); - msg.payload.files[0].should.have.property('fieldname','file'); - msg.payload.files[0].should.have.property('originalname','file.txt'); - msg.payload.files[0].should.have.property('buffer',{"type":"Buffer","data":[72,101,108,108,111,32,87,111,114,108,100]}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({ - headers: { - 'content-type':'multipart/form-data' - }, - payload: { - file: { - value: Buffer.from("Hello World"), - options: { - filename: "file.txt" - } - }, - other: 123 - } - }); - }); - }) - }) - - describe('redirect-cookie', function() { - it('should send cookies to the same domain when redirected(no cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToSameDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['localhost:'+testPort+'/redirectReturn']; - if (cookies1 && Object.keys(cookies1).length != 0) { - done(new Error('Invalid cookie(path:/rediectToSame)')); - return; - } - if ((cookies2 && Object.keys(cookies2).length != 1) || - cookies2['redirectToSameDomainCookie'] !== 'same1') { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToSameDomainCookie.Path.should.equal('/'); - redirect1.cookies.redirectToSameDomainCookie.value.should.equal('same1'); - done(); - } catch(err) { done(err)} - }); - n1.receive({}); - }); - }); - it('should not send cookies to the different domain when redirected(no cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToDifferentDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['127.0.0.1:'+testPort+'/redirectReturn']; - if (cookies1 && Object.keys(cookies1).length != 0) { - done(new Error('Invalid cookie(path:/rediectToDiffer)')); - return; - } - if (cookies2 && Object.keys(cookies2).length != 0) { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect1.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({}); - }); - }); - it('should send cookies to the same domain when redirected(msg.cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToSameDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['localhost:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToSame)')); - return; - } - if ((cookies2 && Object.keys(cookies2).length != 2) || - cookies1['requestCookie'] !== 'request1' || - cookies2['redirectToSameDomainCookie'] !== 'same1') { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToSameDomainCookie.Path.should.equal('/'); - redirect1.cookies.redirectToSameDomainCookie.value.should.equal('same1'); - done(); - }); - n1.receive({ - cookies: { requestCookie: 'request1' } - }); - }); - }); - it('should not send cookies to the different domain when redirected(msg.cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToDifferentDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToDifferentDomain']; - var cookies2 = receivedCookies['127.0.0.1:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToDiffer)')); - return; - } - if (cookies2 && Object.keys(cookies2).length != 0) { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect1.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({ - cookies: { requestCookie: 'request1' } - }); - }); - }); - it('should send cookies to the same domain when redirected(msg.headers.cookie)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToSameDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['localhost:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToSame)')); - return; - } - if ((cookies2 && Object.keys(cookies2).length != 2) || - cookies1['requestCookie'] !== 'request1' || - cookies2['redirectToSameDomainCookie'] !== 'same1') { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToSameDomainCookie.Path.should.equal('/'); - redirect1.cookies.redirectToSameDomainCookie.value.should.equal('same1'); - done(); - }); - n1.receive({ - headers: { cookie: 'requestCookie=request1' } - }); - }); - }); - it('should not send cookies to the different domain when redirected(msg.headers.cookie)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToDifferentDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToDifferentDomain']; - var cookies2 = receivedCookies['127.0.0.1:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToDiffer)')); - return; - } - if (cookies2 && Object.keys(cookies2).length != 0) { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect1.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({ - headers: { cookie: 'requestCookie=request1' } - }); - }); - }); - it('should return all redirect information when redirected multiple times', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectMultipleTimes')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectToDifferentDomain'); - redirect1.cookies.redirectMultipleTimes.Path.should.equal('/'); - redirect1.cookies.redirectMultipleTimes.value.should.equal('multiple1'); - var redirect2 = msg.redirectList[1]; - redirect2.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect2.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect2.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({ - headers: { cookie: 'requestCookie=request1' } - }); - }); - }); - }); -}); diff --git a/test/nodes/core/network/22-websocket_spec.js b/test/nodes/core/network/22-websocket_spec.js deleted file mode 100644 index 995248279..000000000 --- a/test/nodes/core/network/22-websocket_spec.js +++ /dev/null @@ -1,568 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var ws = require("ws"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var websocketNode = require("nr-test-utils").require("@node-red/nodes/core/network/22-websocket.js"); - -var sockets = []; - -function getWsUrl(path) { - return helper.url().replace(/http/, "ws") + path; -} - -function createClient(listenerid) { - return new Promise(function(resolve, reject) { - var node = helper.getNode(listenerid); - var url = getWsUrl(node.path); - var sock = new ws(url); - sockets.push(sock); - - sock.on("open", function() { - resolve(sock); - }); - - sock.on("error", function(err) { - reject(err); - }); - }); -} - -function closeAll() { - for (var i = 0; i < sockets.length; i++) { - sockets[i].close(); - } - sockets = []; -} - -function getSocket(listenerid) { - var node = helper.getNode(listenerid); - return node.server; -} - -describe('websocket Node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - closeAll(); - helper.unload(); - }); - - describe('websocket-listener', function() { - it('should load', function(done) { - var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property("path", "/ws"); - done(); - }); - }); - - it('should be server', function(done) { - var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property('isServer', true); - done(); - }); - }); - - it('should handle wholemsg property', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket-listener", path: "/ws2", wholemsg: "true" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property("wholemsg", false); - helper.getNode("n2").should.have.property("wholemsg", true); - done(); - }); - }); - - it('should create socket', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket in", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should close socket on delete', function(done) { - var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("close", function(code, msg) { - done(); - }); - helper.clearFlows(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive data', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - sock.send("hello"); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive wholemsg', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.send('{"text":"hello"}'); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("text", "hello"); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive wholemsg when data not JSON', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.send('hello'); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive wholemsg when data not object', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", 123); - done(); - }); - sock.send(123); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should send', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "helper", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - msg.should.equal("hello"); - done(); - }); - helper.getNode("n2").send({ - payload: "hello" - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should send wholemsg', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket out", server: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - JSON.parse(msg).should.have.property("text", "hello"); - done(); - }); - helper.getNode("n3").send({ - text: "hello" - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should do nothing if no payload', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "helper", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - setTimeout(function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - logEvents.should.have.length(0); - done(); - },100); - helper.getNode("n2").send({topic: "hello"}); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should echo', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - msg.should.equal("hello"); - done(); - }); - sock.send("hello"); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should echo wholemsg', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - JSON.parse(msg).should.have.property("text", "hello"); - done(); - }); - sock.send('{"text":"hello"}'); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should broadcast', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket out", server: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - Promise.all([createClient("n1"), createClient("n1")]).then(function(socks) { - var promises = [ - new Promise((resolve,reject) => { - socks[0].on("message", function(msg, flags) { - try { - msg.should.equal("hello"); - resolve(); - } catch(err) { - reject(err); - } - }); - }), - new Promise((resolve,reject) => { - socks[1].on("message", function(msg, flags) { - try { - msg.should.equal("hello"); - resolve(); - } catch(err) { - reject(err); - } - }); - }) - ]; - helper.getNode("n3").send({ - payload: "hello" - }); - return Promise.all(promises).then(() => {done()}); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - describe('websocket-client', function() { - it('should load', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property('path', getWsUrl("/ws")); - done(); - }); - }); - - it('should not be server', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property('isServer', false); - done(); - }); - }); - - it('should handle wholemsg property', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property("wholemsg", false); - helper.getNode("n2").should.have.property("wholemsg", true); - done(); - }); - }); - - it('should connect to server', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - done(); - }); - - }); - }); - - it('should close on delete', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('close', function() { - done(); - }); - helper.getNode("n2").close(); - }); - }); - }); - - it('should receive data', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket in", client: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.send('hello'); - }); - - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - }); - }); - - it('should receive wholemsg data ', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n2", type: "websocket in", client: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.send('{"text":"hello"}'); - }); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("text", "hello"); - done(); - }); - }); - }); - - it('should receive wholemsg when data not JSON', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n2", type: "websocket in", client: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.send('hello'); - }); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - }); - }); - - it('should send', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket out", client: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('message', function(msg) { - msg.should.equal("hello"); - done(); - }); - }); - getSocket("n1").on("open", function() { - helper.getNode("n3").send({ - payload: "hello" - }); - }); - }); - }); - - it('should send buffer', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket out", client: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('message', function(msg) { - Buffer.isBuffer(msg).should.be.true(); - msg.should.have.length(5); - done(); - }); - }); - getSocket("n1").on("open", function() { - helper.getNode("n3").send({ - payload: Buffer.from("hello") - }); - }); - }); - }); - - it('should send wholemsg', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n2", type: "websocket out", client: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('message', function(msg) { - JSON.parse(msg).should.have.property("text", "hello"); - done(); - }); - }); - getSocket("n1").on('open', function(){ - helper.getNode("n3").send({ - text: "hello" - }); - }); - }); - }); - - it('should NOT feedback more than once', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "client", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n1", type: "websocket in", client: "client", wires: [["n2", "output"]] }, - { id: "n2", type: "websocket out", server: "server" }, - { id: "n3", type: "helper", wires: [["n2"]] }, - { id: "output", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('client').on('open', function() { - helper.getNode("n3").send({ - payload: "ping" - }); - }); - var acc = 0; - helper.getNode("output").on("input", function(msg) { - acc = acc + 1; - }); - setTimeout( function() { - acc.should.equal(1); - helper.clearFlows(); - done(); - }, 250); - }); - }); - }); - - describe('websocket in node', function() { - it('should report error if no server config', function(done) { - var flow = [{ id: "n1", type: "websocket in", mode: "server" }]; - helper.load(websocketNode, flow, function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "websocket in"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("websocket.errors.missing-conf"); - done(); - }); - }); - }); - - describe('websocket out node', function() { - it('should report error if no server config', function(done) { - var flow = [{ id: "n1", type: "websocket out", mode: "server" }]; - helper.load(websocketNode, flow, function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "websocket out"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("websocket.errors.missing-conf"); - done(); - }); - }); - }); -}); diff --git a/test/nodes/core/network/31-tcpin_spec.js b/test/nodes/core/network/31-tcpin_spec.js deleted file mode 100644 index cbe7ced2a..000000000 --- a/test/nodes/core/network/31-tcpin_spec.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var should = require("should"); -var stoppable = require('stoppable'); -var helper = require("node-red-node-test-helper"); - -var tcpinNode = require("nr-test-utils").require("@node-red/nodes/core/network/31-tcpin.js"); - - -describe('TCP in Node', function() { - var port = 9200; - var server = undefined; - var server_port = 9300; - var reply_data = undefined; - - beforeEach(function(done) { - startServer(done); - }); - - afterEach(function(done) { - helper.unload(); - stopServer(done); - }); - - function sendArray(sock, array) { - if(array.length > 0) { - sock.write(array[0], function() { - sendArray(sock, array.slice(1)); - }); - } - else { - sock.end(); - } - } - - function startServer(done) { - server_port += 1; - server = stoppable(net.createServer(function(c) { - sendArray(c, reply_data); - })).listen(server_port, "localhost", function(err) { - done(err); - }); - } - - function stopServer(done) { - server.stop(done); - } - - function send(wdata) { - var opt = {port:port, host:"localhost"}; - var client = net.createConnection(opt, function() { - client.write(wdata[0], function() { - client.end(); - if(wdata.length > 1) { - send(wdata.slice(1)); - } - }); - }); - } - - function eql(v0, v1) { - return((v0 === v1) || ((typeof v0) === 'object' && v0.equals(v1))); - } - - function testTCP(flow, wdata, rdata, is_server, done) { - if(is_server) { - reply_data = wdata; - } - helper.load(tcpinNode, flow, function() { - var n2 = helper.getNode("n2"); - var rcount = 0; - n2.on("input", function(msg) { - if(eql(msg.payload, rdata[rcount])) { - rcount++; - } - else { - should.fail(); - } - if(rcount === rdata.length) { - done(); - } - }); - if(!is_server) { - send(wdata); - } - }); - } - - function testTCP0(flow, wdata, rdata, done) { - testTCP(flow, wdata, rdata, false, done); - } - - function testTCP1(flow, wdata, rdata, done) { - testTCP(flow, wdata, rdata, true, done); - } - - it('should recv data (Stream/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should recv data (Stream/String/Delimiter:\\n)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo\nbar"], ["foo", "bar"], done); - }); - - it('should recv data (Stream/String/No delimiter)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo\nbar"], ["foo\nbar"], done); - }); - - it('should recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - - it('should recv data (Single/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should recv data (Single/String)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo\nbar\nbaz"], ["foo\nbar\nbaz"], done); - }); - - it('should recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - - it('should recv multiple data (Stream/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo", "bar"], [Buffer("foo"), Buffer("bar")], done); - }); - - it('should recv multiple data (Stream/String/Delimiter:\\n)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo", "bar\nbaz"], ["foo", "bar", "baz"], done); - }); - - it('should recv multiple data (Stream/String/No delimiter)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo", "bar\nbaz"], ["foo", "bar\nbaz"], done); - }); - - it('should recv multiple data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - var wdata = ["foo", "bar"]; - var rdata = wdata.map(function(x) { - return Buffer(x).toString('base64'); - }); - testTCP0(flow, wdata, rdata, done); - }); - - it('should connect & recv data (Stream/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should connect & recv data (Stream/String/Delimiter:\\n)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo\nbar"], ["foo", "bar"], done); - }); - - it('should connect & recv data (Stream/String/No delimiter)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo\nbar"], ["foo\nbar"], done); - }); - - it('should connect & recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - - it('should connect & recv data (Single/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should connect & recv data (Single/String)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo\nbar\nbaz"], ["foo\nbar\nbaz"], done); - }); - - it('should connect & recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - -}); diff --git a/test/nodes/core/network/31-tcprequest_spec.js b/test/nodes/core/network/31-tcprequest_spec.js deleted file mode 100644 index dc0755c1b..000000000 --- a/test/nodes/core/network/31-tcprequest_spec.js +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var should = require("should"); -var stoppable = require('stoppable'); -var helper = require("node-red-node-test-helper"); -var tcpinNode = require("nr-test-utils").require("@node-red/nodes/core/network/31-tcpin.js"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); - - -describe('TCP Request Node', function() { - var server = undefined; - var port = 9000; - - function startServer(done) { - port += 1; - server = stoppable(net.createServer(function(c) { - c.on('data', function(data) { - var rdata = "ACK:"+data.toString(); - c.write(rdata); - }); - c.on('error', function(err) { - startServer(done); - }); - })).listen(port, "127.0.0.1", function(err) { - done(); - }); - } - - before(function(done) { - startServer(done); - }); - - after(function(done) { - server.stop(done); - }); - - afterEach(function() { - helper.unload(); - }); - - function testTCP(flow, val0, val1, done) { - helper.load(tcpinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - if (typeof val1 === 'object') { - msg.should.have.properties(Object.assign({}, val1, {payload: Buffer.from(val1.payload)})); - } else { - msg.should.have.property('payload', Buffer.from(val1)); - } - done(); - } catch(err) { - done(err); - } - }); - if((typeof val0) === 'object') { - n1.receive(val0); - } else { - n1.receive({payload:val0}); - } - }); - } - - function testTCPMany(flow, values, result, done) { - helper.load(tcpinNode, flow, () => { - const n1 = helper.getNode("n1"); - const n2 = helper.getNode("n2"); - n2.on("input", msg => { - try { - if (typeof result === 'object') { - msg.should.have.properties(Object.assign({}, result, {payload: Buffer.from(result.payload)})); - } else { - msg.should.have.property('payload', Buffer.from(result)); - } - done(); - } catch(err) { - done(err); - } - }); - values.forEach(value => { - n1.receive(typeof value === 'object' ? value : {payload: value}); - }); - }); - } - - describe('single message', function () { - it('should send & recv data', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should retain complete message', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & recv data when specified character received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo0bar0', - topic: 'bar' - }, { - payload: 'ACK:foo0', - topic: 'bar' - }, done); - }); - - it('should send & recv data after fixed number of chars received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo bar', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & receive, then keep connection', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & recv data to/from server:port from msg', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: "foo", - host: "localhost", - port: port - }, { - payload: "ACK:foo", - host: 'localhost', - port: port - }, done); - }); - }); - - describe('many messages', function () { - it('should send & recv data', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: 'f', - topic: 'bar' - }, { - payload: 'o', - topic: 'bar' - }, { - payload: 'o', - topic: 'bar' - }], { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & recv data when specified character received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "foo0", - topic: 'bar' - }, { - payload: "bar0", - topic: 'bar' - }], { - payload: "ACK:foo0", - topic: 'bar' - }, done); - }); - - it('should send & recv data after fixed number of chars received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "fo", - topic: 'bar' - }, { - payload: "ob", - topic: 'bar' - }, { - payload: "ar", - topic: 'bar' - }], { - payload: "ACK:foo", - topic: 'bar' - }, done); - }); - - it('should send & receive, then keep connection', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "foo", - topic: 'bar' - }, { - payload: "bar", - topic: 'bar' - }, { - payload: "baz", - topic: 'bar' - }], { - payload: "ACK:foobarbaz", - topic: 'bar' - }, done); - }); - - it('should send & recv data to/from server:port from msg', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "f", - host: "localhost", - port: port - }, - { - payload: "o", - host: "localhost", - port: port - }, - { - payload: "o", - host: "localhost", - port: port - } - ], { - payload: "ACK:foo", - host: 'localhost', - port: port - }, done); - }); - - it('should limit the queue size', function (done) { - RED.settings.tcpMsgQueueSize = 10; - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - // create one more msg than is allowed - const msgs = new Array(RED.settings.tcpMsgQueueSize + 1).fill('x'); - const expected = msgs.slice(0, -1); - testTCPMany(flow, msgs, "ACK:" + expected.join(''), done); - }); - - it('should only retain the latest message', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: 'f', - topic: 'bar' - }, { - payload: 'o', - topic: 'baz' - }, { - payload: 'o', - topic: 'quux' - }], { - payload: 'ACK:foo', - topic: 'quux' - }, done); - }); - }); -}); diff --git a/test/nodes/core/network/32-udpin_spec.js b/test/nodes/core/network/32-udpin_spec.js deleted file mode 100644 index 107c313bf..000000000 --- a/test/nodes/core/network/32-udpin_spec.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var dgram = require("dgram"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var udpNode = require("nr-test-utils").require("@node-red/nodes/core/network/32-udp.js"); - - -describe('UDP in Node', function() { - var port = 9100; - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - function sendIPv4(msg) { - var sock = dgram.createSocket('udp4'); - sock.send(msg, 0, msg.length, port, "127.0.0.1", function(msg) { - sock.close(); - }); - } - - function checkRecv(dt, proto, val0, val1, done) { - var flow = [{id:"n1", type:"udp in", - group: "", multicast:false, - port:port, ipv:proto, - datatype: dt, iface: "", - wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - helper.load(udpNode, flow, function() { - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - var ip = ((proto === 'udp6') ? '::ffff:':'') +'127.0.0.1'; - msg.should.have.property('ip', ip); - msg.should.have.property('port'); - msg.should.have.property('payload'); - msg.payload.should.deepEqual(val1); - done(); - } catch(err) { - done(err); - } - }); - sendIPv4(val0); - }); - } - - it('should recv IPv4 data (Buffer)', function(done) { - checkRecv('buffer', 'udp4', 'hello', Buffer('hello'), done); - }); - - it('should recv IPv4 data (String)', function(done) { - checkRecv('utf8', 'udp4', 'hello', 'hello', done); - }); - - it('should recv IPv4 data (base64)', function(done) { - checkRecv('base64', 'udp4', 'hello', Buffer('hello').toString('base64'), done); - }); - - it('should recv IPv6 data (Buffer)', function(done) { - checkRecv('buffer', 'udp6', 'hello', Buffer('hello'), done); - }); - - it('should recv IPv6 data (String)', function(done) { - checkRecv('utf8', 'udp6', 'hello', 'hello', done); - }); - - it('should recv IPv6 data (base64)', function(done) { - checkRecv('base64', 'udp6', 'hello', Buffer('hello').toString('base64'), done); - }); - -}); diff --git a/test/nodes/core/network/32-udpout_spec.js b/test/nodes/core/network/32-udpout_spec.js deleted file mode 100644 index ff01c2f10..000000000 --- a/test/nodes/core/network/32-udpout_spec.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var dgram = require("dgram"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var udpNode = require("nr-test-utils").require("@node-red/nodes/core/network/32-udp.js"); - - -describe('UDP out Node', function() { - var port = 9200; - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - function recvData(data, done) { - var sock = dgram.createSocket('udp4'); - sock.on('message', function(msg, rinfo) { - sock.close(done); - msg.should.deepEqual(data); - }); - sock.bind(port, '127.0.0.1'); - port++; - } - - function checkSend(proto, val0, val1, decode, dest_in_msg, done) { - var dst_ip = dest_in_msg ? undefined : "127.0.0.1"; - var dst_port = dest_in_msg ? undefined : port; - var flow = [{id:"n1", type:"udp out", - addr:dst_ip, port:dst_port, iface: "", - ipv:proto, outport: "", - base64:decode, multicast:false, - wires:[] }]; - helper.load(udpNode, flow, function() { - var n1 = helper.getNode("n1"); - var msg = {}; - if (decode) { - msg.payload = Buffer.from("hello").toString('base64'); - } - else { - msg.payload = "hello"; - } - if (dest_in_msg) { - msg.ip = "127.0.0.1"; - msg.port = port; - } - recvData(val1, done); - setTimeout(function() { - n1.receive(msg); - }, 200); - }); - } - - it('should send IPv4 data', function(done) { - checkSend('udp4', 'hello', Buffer.from('hello'), false, false, done); - }); - - it('should send IPv4 data (base64)', function(done) { - checkSend('udp4', 'hello', Buffer.from('hello'), true, false, done); - }); - - it('should send IPv4 data with dest from msg', function(done) { - checkSend('udp4', 'hello', Buffer.from('hello'), false, true, done); - }); - -}); diff --git a/test/nodes/core/parsers/70-CSV_spec.js b/test/nodes/core/parsers/70-CSV_spec.js deleted file mode 100644 index cb8d7ca09..000000000 --- a/test/nodes/core/parsers/70-CSV_spec.js +++ /dev/null @@ -1,1062 +0,0 @@ -/* eslint-disable no-undef */ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -// var should = require("should"); -var csvNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-CSV.js"); -var helper = require("node-red-node-test-helper"); -// const { neq } = require("semver"); - -describe('CSV node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded with defaults', function(done) { - var flow = [{id:"csvNode1", type:"csv", name: "csvNode" }]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("csvNode1"); - n1.should.have.property('name', 'csvNode'); - n1.should.have.property('template',''); - n1.should.have.property('sep', ','); - n1.should.have.property('quo', '"'); - n1.should.have.property('ret', '\n'); - n1.should.have.property('winflag', false); - n1.should.have.property('lineend', '\n'); - n1.should.have.property('multi', 'one'); - n1.should.have.property('hdrin', false); - done(); - }); - }); - - describe('csv to json', function() { - var parts_id = undefined; - - afterEach(function() { - parts_id = undefined; - }); - - function check_parts(msg, index, count) { - msg.should.have.property('parts'); - if(parts_id === undefined) { - parts_id = msg.parts.id; - } - else { - msg.parts.should.have.property('id', parts_id); - } - msg.parts.should.have.property('index', index); - msg.parts.should.have.property('count', count); - } - - it('should convert a simple csv string to a javascript object', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); - msg.should.have.property('columns', "a,b,c,d"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should convert a simple string to a javascript object with | separator (no template)', function(done) { - var flow = [ { id:"n1", type:"csv", sep:"|", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { col1: 1, col2: 2, col3: 3, col4: 4 }); - msg.should.have.property('columns', "col1,col2,col3,col4"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1|2|3|4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should convert a simple string to a javascript object with tab separator (with template)', function(done) { - var flow = [ { id:"n1", type:"csv", sep:"\t", temp:"A,B,,D", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { A: 1, B: 2, D: 4 }); - msg.should.have.property('columns', "A,B,D"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1\t2\t3\t4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should convert a simple string to a javascript object with space separator (with spaced template)', function(done) { - var flow = [ { id:"n1", type:"csv", sep:" ", temp:"A, B, , D", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { A: 1, B: 2, D: 4 }); - msg.should.have.property('columns', "A,B,D"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1 2 3 4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should remove quotes and whitespace from template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:'"a", "b" , " c "," d " ', wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should create column names if no template provided', function(done) { - var flow = [ { id:"n1", type:"csv", temp:'', wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { col1: 1, col2: 2, col3: 3, col4: 4 }); - msg.should.have.property('columns', "col1,col2,col3,col4"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow dropping of fields from the template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,,,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, d: 4 }); - msg.should.have.property('columns', 'a,d'); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow commas and spaces in the template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b b,\"c,c\",\" d, d \"", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,"c,c","d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,"c,c","d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a,b b,"c,c"," d, d "'+"\n"+"1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV (not comma)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, sep:";", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c;c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,c;c,"d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a;b b;"c;c";" d, d "'+"\n"+"1;2;3;4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV (special char /)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, sep:"/", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c/c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,c/c,"d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a/b b/"c/c"/" d, d "'+"\n"+"1/2/3/4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV (special char \\)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, sep:"\\", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c\\c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,c\\c,"d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a\\b b\\"c\\c"\\" d, d "'+"\n"+"1\\2\\3\\4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should leave numbers starting with 0, e and + as strings (except 0.)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 123, b: "0123", c: '+123', d: 'e123', e: 'E123', f: -123 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '123,0123,+123,e123,E123,-123'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should not parse numbers when told not to do so', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", strings:false, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: "1.23", b: "0123", c: "+123", d: "e123", e: "0", f: "-123", g: "1e3" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '1.23,0123,+123,e123,0,-123,1e3'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should parse numbers when told to do so', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1.23, b: -123, c: 1000, d: 0 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = ' 1.23 , -123,1e3 , 0 '+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should leave handle strings with scientific notation as numbers', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 12000, b: 0.012, c: -12000, d: -0.012 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '12E3,12e-3,-12e3,-12E-3'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - - it('should allow quotes in the input (but drop blank strings)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g,h", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a:1, b:-2, c:'+3', d:'04', f:'-05', g:'ab"cd', h:'with,a,comma' }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '"1","-2","+3","04","","-05","ab""cd","with,a,comma"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow blank strings in the input if selected', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", include_empty_strings:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a: 1, b: '', c: '', d: '', e: '-05', f: 'ab"cd', g: 'with,a,comma' }); - //check_parts(msg, 0, 1); - done(); - }); - var testString = '"1","","","","-05","ab""cd","with,a,comma"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow missing columns (nulls) in the input if selected', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", include_null_values:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a: 1, b: null, c: '+3', d: null, e: '-05', f: 'ab"cd', g: 'with,a,comma' }); - //check_parts(msg, 0, 1); - done(); - }); - var testString = '"1",,"+3",,"-05","ab""cd","with,a,comma"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should handle cr and lf in the input', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a: "with a\nnew line", b: "and a\rcarriage return", c: "and why\r\nnot both"}); - check_parts(msg, 0, 1); - done(); - }); - var testString = '"with a'+String.fromCharCode(10)+'new line","and a'+String.fromCharCode(13)+'carriage return","and why\r\nnot both"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should recover from an odd number of quotes in the input', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c == 0) { - c = 1; - msg.should.have.property('payload', { a: "with,an", b: "odd,number", c: "ofquotes\n" }); - check_parts(msg, 0, 1); - } - else { - msg.should.have.property('payload', { a: "this is", b: "a normal", c: "line" }); - check_parts(msg, 0, 1); - done(); - } - }); - var testString = '"with,a"n,odd","num"ber","of"qu"ot"es"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - n1.emit("input", {payload:'"this is","a normal","line"'}); - }); - }); - - it('should recover from an odd number of quotes in the input (2)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - //console.log(msg) - if (c == 0) { - c = 1; - msg.should.have.property('payload', { a: "with,an", b: "odd,number", c: "ofquotes\nthis is,a normal,line" }); - check_parts(msg, 0, 1); - } - else { - msg.should.have.property('payload', { a: "this is", b: "another", c: "line" }); - check_parts(msg, 0, 1); - done(); - } - }); - var testString = '"with,a"n,odd","num"ber","of"qu"ot"es"'+String.fromCharCode(10)+'"this is","a normal","line"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - n1.emit("input", {payload:'"this is","another","line"'}); - }); - }); - - it('should be able to use the first line as a template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrin:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - //console.log(msg); - if (c === 0) { - msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); - check_parts(msg, 0, 2); - c += 1; - } - else { - msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); - check_parts(msg, 1, 2); - done(); - } - }); - var testString = "w,x,y,z\n1,2,3,4\n\n5,6,7,8"; - n1.emit("input", {payload:testString}); - }); - }); - - it('should be able to output multiple lines as one array', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", multi:"yes", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', [ { a: 1, b: 2, c: 3, d: 4 },{ a: 5, b: -6, c: '07', d: '+8' },{ a: 9, b: 0, c: 'a', d: 'b' },{ a: 'c', b: 'd', c: 'e', d: 'f' } ]); - msg.should.have.property('columns','a,b,c,d'); - msg.should.not.have.property('parts'); - done(); - }); - var testString = "1,2,3,4\n5,-6,07,+8\n9,0,a,b\nc,d,e,f"; - n1.emit("input", {payload:testString}); - }); - }); - - it('should be able to create an array from multiple parts', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, multi:"mult", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', [{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]); - msg.should.have.property('columns','a,b,c'); - msg.should.not.have.property('parts'); - done(); - }); - - n1.emit("input", {"payload":"a,b,c","parts":{"index":0,"ch":"\n","type":"string","id":"1"}}); - n1.emit("input", {"payload":"1,2,3","parts":{"index":1,"ch":"\n","type":"string","id":"1"}}); - n1.emit("input", {"payload":"4,5,6","parts":{"index":2,"ch":"\n","type":"string","id":"1"}}); - n1.emit("input", {"payload":"7,8,9","parts":{"index":3,count:4,"ch":"\n","type":"string","id":"1"}}); - }); - }); - - it('should be able to output multiple objects as an array from an input of parts', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, multi:"yes", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', [{"Col1":"V1","Col2":"V2"},{"Col1":"V3","Col2":"V4"},{"Col1":"V5","Col2":"V6"}]); - msg.should.have.property('columns','Col1,Col2'); - msg.should.have.property('parts'); - done(); - }); - //var testString = "1,2,3,4\n5,-6,07,+8\n9,0,a,b\nc,d,e,f"; - // n1.emit("input", {payload:testString}); - n1.emit("input", {"payload":"Col1,Col2\nV1,V2\nV3,V4\nV5,V6","topic":"","parts":{"id":"3af07e18.865652","type":"array","count":2,"len":1,"index":0}}); - //n1.emit("input", {"payload":"Var1,Var2\nW1,W2\nW3,W4\nW5,W6","topic":"","parts":{"id":"3af07e18.865652","type":"array","count":2,"len":1,"index":1}}); - }); - }); - - it('should handle numbers in strings but not IP addresses', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: "a", b: "127.0.0.1", c: 56.7, d: -32.8, e: "+76.22C" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "a,127.0.0.1,56.7,-32.8,+76.22C"; - n1.emit("input", {payload:testString}); - }); - }); - - it('should preserve parts property', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); - check_parts(msg, 3, 4); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString, parts: {id:"X", index:3, count:4} }); - }); - }); - - it('should be able to use the first of multiple parts as a template if parts are present', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); - check_parts(msg, 0, 2); - c += 1; - } - else { - msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); - check_parts(msg, 1, 2); - done(); - } - }); - var testString1 = "w,x,y,z\n"; - var testString2 = "1,2,3,4\n"; - var testString3 = "5,6,7,8\n"; - n1.emit("input", {payload:testString1, parts:{id:"X", index:0, count:3}}); - n1.emit("input", {payload:testString2, parts:{id:"X", index:1, count:3}}); - n1.emit("input", {payload:testString3, parts:{id:"X", index:2, count:3}}); - }); - }); - - it('should skip several lines from start if requested', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 9, b: 0, c: "A", d: "B" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should skip several lines from start then use next line as a template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrin:true, skip: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { "9": "C", "0": "D", "A": "E", "B": "F" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10)+"C,D,E,F"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should skip several lines from start and correct parts', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c===0) { - msg.should.have.property('payload', { a: 9, b: 0, c: "A", d: "B" }); - check_parts(msg, 0, 2); - c = c+1; - } - else { - msg.should.have.property('payload', { a: "C", b: "D", c: "E", d: "F" }); - check_parts(msg, 1, 2); - done(); - } - }); - var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10)+"C,D,E,F"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should be able to skip and then use the first of multiple parts as a template if parts are present', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, skip:2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); - msg.should.have.property('columns', 'w,x,y,z'); - check_parts(msg, 0, 2); - c += 1; - } - else { - msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); - msg.should.have.property('columns', 'w,x,y,z'); - check_parts(msg, 1, 2); - done(); - } - }); - var testStringA = "foo\n"; - var testStringB = "bar\n"; - var testString1 = "w,x,y,z\n"; - var testString2 = "1,2,3,4\n"; - var testString3 = "5,6,7,8\n"; - n1.emit("input", {payload:testStringA, parts:{id:"X", index:0, count:5}}); - n1.emit("input", {payload:testStringB, parts:{id:"X", index:1, count:5}}); - n1.emit("input", {payload:testString1, parts:{id:"X", index:2, count:5}}); - n1.emit("input", {payload:testString2, parts:{id:"X", index:3, count:5}}); - n1.emit("input", {payload:testString3, parts:{id:"X", index:4, count:5}}); - }); - }); - - }); - - describe('json object to csv', function() { - - it('should convert a simple object back to a csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '4,foo,true,,0\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { e:0, d:1, b:"foo", c:true, a:4 }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert a simple object back to a csv with no template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:" ", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '1,foo,"ba""r","di,ng"\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert a simple object back to a tsv using a tab as a separator', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", sep:"\t", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '1\tfoo\t"ba""r"\tdi,ng\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should handle a template with spaces in the property names', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b o,c p,,e", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '4,foo,true,,0\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { e:0, d:1, "b o":"foo", "c p":true, a:4 }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,d,c,b", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '4,1,2,3\n1,4,3,2\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv and add a header', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrout:"all", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'a,b,c,d\n4,3,2,1\n1,2,3,4\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv without a template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '1,3,2,4\n4,2,3,1\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv without a template and with a header', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"all", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'd,b,c,a\n1,3,2,4\n4,2,3,1\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert a simple array back to a csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', ',0,1,foo,"ba""r","di,ng"\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = ["",0,1,"foo",'ba"r','di,ng']; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of arrays back to a multi-line csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '0,1,2,3,4\n4,3,2,1,0\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [[0,1,2,3,4],[4,3,2,1,0]]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should be able to include column names as first row', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrout:true, ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'a,b,c,d\r\n4,3,2,1\r\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 }]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should be able to include column names as first row, and missing properties', function(done) { - var flow = [ { id:"n1", type:"csv", hdrout:true, ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'col1,col2,col3,col4\r\nH1,H2,H3,H4\r\nA,B,,\r\nA,,C,\r\nA,,,D\r\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{"col1":"H1","col2":"H2","col3":"H3","col4":"H4"},{"col1":"A","col2":"B"},{"col1":"A","col3":"C"},{"col1":"A","col4":"D"}]; - n1.emit("input", {payload:testJson}); - }); - }); - - - it('should be able to pass in column names', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - count += 1; - try { - if (count === 1) { - msg.should.have.property('payload', 'a,,b,a\r\n4,,3,4\r\n'); - } - if (count === 3) { - msg.should.have.property('payload', '4,,3,4\r\n'); - done() - } - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 }]; - n1.emit("input", {payload:testJson, columns:"a,,b,a", parts:{index:0}}); - n1.emit("input", {payload:testJson, parts:{index:1}}); - n1.emit("input", {payload:testJson, parts:{index:2}}); - }); - }); - - it('should be able to pass in column names - with payload as an array', function(done) { - var flow = [ { id:"n1", type:"csv", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'a,,b,a\r\n4,,3,4\r\n4,,3,4\r\n4,,3,4\r\n'); - done() - } - catch(e) { done(e); } - }); - var testJson = { d: 1, b: 3, c: 2, a: 4 }; - n1.emit("input", {payload:[testJson,testJson,testJson], columns:"a,,b,a"}); - }); - }); - - it('should handle quotes and sub-properties', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '{},"text,with,commas","This ""is"" a banana","{""sub"":""object""}"\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { d: {sub:"object"}, b: "text,with,commas", c: 'This "is" a banana', a: {sub2:undefined} }; - n1.emit("input", {payload:testJson}); - }); - }); - - }); - - it('should just pass through if no payload provided', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', { a: 4, b: 3, c: 2, d: 1 }); - msg.should.not.have.property('payload'); - - done(); - } - catch(e) { done(e); } - }); - var testJson = { d: 1, b: 3, c: 2, a: 4 }; - n1.emit("input", {topic:testJson}); - }); - }); - - it('should warn if provided a number or boolean', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "csv"; - }); - logEvents.should.have.length(2); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith('csv.errors.csv_js'); - logEvents[1][0].should.have.a.property('msg'); - logEvents[1][0].msg.toString().should.startWith('csv.errors.csv_js'); - done(); - } catch(err) { - done(err); - } - },150); - n1.emit("input", {payload:1}); - n1.emit("input", {payload:true}); - }); - }); - - it('should call done when message processing is completed', function(done) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[[]]}, - { id:"c1", type:"complete", scope: ["n1"], uncaught:false, wires:[["h1"]]}, - { id:"h1", type:"helper", wires:[[]]} ]; - helper.load([csvNode,completeNode], flow, function() { - const n1 = helper.getNode("n1"); - const h1 = helper.getNode("h1"); - h1.on("input", function(msg) { - try { - msg.should.have.a.property('payload', "1,2,3,4"); - done(); - } catch (e) { - done(e); - } - }); - n1.receive({payload:"1,2,3,4"}); - }); - }); - - it('should call done when input causes an error', function(done) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[[]]}, - { id:"c1", type:"complete", scope: ["n1"], uncaught:false, wires:[["h1"]]}, - { id:"h1", type:"helper", wires:[[]]} ]; - helper.load([csvNode,completeNode], flow, function() { - const n1 = helper.getNode("n1"); - const h1 = helper.getNode("h1"); - h1.on("input", function(msg) { - try { - msg.should.have.a.property('payload', 1); - done(); - } catch (e) { - done(e); - } - }); - n1.receive({payload:1}); // neither object nor string - }); - }); -}); diff --git a/test/nodes/core/parsers/70-HTML_spec.js b/test/nodes/core/parsers/70-HTML_spec.js deleted file mode 100644 index da7246e64..000000000 --- a/test/nodes/core/parsers/70-HTML_spec.js +++ /dev/null @@ -1,494 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var path = require("path"); -var fs = require('fs-extra'); - -var htmlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-HTML.js"); -var helper = require("node-red-node-test-helper"); - -describe('HTML node', function() { - - var resourcesDir = __dirname+ path.sep + ".." + path.sep + ".." + path.sep + ".." + path.sep + "resources" + path.sep; - var file = path.join(resourcesDir, "70-HTML-test-file.html"); - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - beforeEach(function() { - fs.existsSync(file).should.be.true(); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"htmlNode1", type:"html", name: "htmlNode" }]; - helper.load(htmlNode, flow, function() { - var htmlNode1 = helper.getNode("htmlNode1"); - htmlNode1.should.have.property('name', 'htmlNode'); - done(); - }); - }); - - it('should retrieve header contents if asked to by msg.select', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - should.equal(msg.payload, 'This is a test page for node 70-HTML'); - done(); - } catch(err) { - done(err) - } - }); - // include 'body' in the select to verify we're in document mode - // for the parser. See https://github.com/node-red/node-red/issues/3079 - n1.receive({payload:data,topic:"bar",select:"body h1"}); - }); - }); - }); - - it('should retrieve header contents if asked to by msg.select - alternative in property', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.foo[0].should.equal('This is a test page for node 70-HTML'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({foo:data,topic:"bar",select:"h1"}); - }); - }); - }); - - it('should retrieve header contents if asked to by msg.select - alternative in and out properties', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",property:"foo",outproperty:"bar",tag:"h1",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.bar[0].should.equal('This is a test page for node 70-HTML'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({foo:data,topic:"bar"}); - }); - }); - }); - - it('should emit an empty array if no matching elements', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload'); - msg.payload.should.be.empty; - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic:"bar",select:"h4"}); - }); - }); - }); - - it('should retrieve paragraph contents when specified', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"text",tag:"p"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - should.equal(msg.payload, 'There\'s nothing to read here.'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve list contents as an array of html as default', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ol"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.payload[0].indexOf("
  • Blue
  • ").should.be.above(-1); - msg.payload[0].indexOf("
  • Red
  • ").should.be.above(-1); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve list contents as an array of text', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ol",ret:"text"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.payload[0].indexOf("Blue").should.be.above(-1); - msg.payload[0].indexOf("Red").should.be.above(-1); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should fix up a unclosed tag', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"span"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - should.equal(msg.payload, ''); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve an attribute from a tag', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"attr",tag:"span img"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload[0].should.have.property('src','foo.png'); - msg.should.have.property('topic', 'bar'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should log on error', function(done) { - fs.readFile(file,function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"p"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - try { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:null,topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "html"; - }); - logEvents.should.have.length(1); - // Each logEvent is the array of args passed to the function. - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should pass through if payload empty', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({topic: "bar"}); - }); - }); - }); - - describe('multiple messages', function(){ - var cnt = 0; - var parts_id = undefined; - - afterEach(function() { - cnt.should.be.exactly(2); - cnt = 0; - parts_id = undefined; - }); - - function check_parts(msg, index, count) { - msg.should.have.property('parts'); - msg.parts.should.have.property('id'); - if(parts_id === undefined) { - parts_id = msg.parts.id; - } - else { - msg.parts.should.have.property('id', parts_id); - } - msg.parts.should.have.property('index', index); - msg.parts.should.have.property('count', count); - msg.parts.should.have.property('type', 'string'); - msg.parts.should.have.property('ch', ''); - } - - it('should retrieve list contents as html as default with output as multiple msgs', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - cnt++; - msg.should.have.property('topic', 'bar'); - check_parts(msg, cnt -1, 2); - if (cnt !== 1 && cnt !== 2) { - return false; - } - if (cnt === 1) { - msg.payload.indexOf("
  • Apple
  • ").should.be.above(-1); - msg.payload.indexOf("
  • Pear
  • ").should.be.above(-1); - } else if (cnt === 2) { - msg.payload.indexOf("
  • Potato
  • ").should.be.above(-1); - msg.payload.indexOf("
  • Parsnip
  • ").should.be.above(-1); - done(); - } - } catch(err) { - done(err) - } - - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - - it('should retrieve list contents as html as default with output as multiple msgs - alternative property', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],tag:"ul",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - cnt++; - msg.should.have.property('topic', 'bar'); - check_parts(msg, cnt -1, 2); - if (cnt !== 1 && cnt !== 2) { - return false; - } - if (cnt === 1) { - msg.foo.indexOf("
  • Apple
  • ").should.be.above(-1); - msg.foo.indexOf("
  • Pear
  • ").should.be.above(-1); - } else if (cnt === 2) { - msg.foo.indexOf("
  • Potato
  • ").should.be.above(-1); - msg.foo.indexOf("
  • Parsnip
  • ").should.be.above(-1); - done(); - } - } catch(err) { - done(err) - } - }); - n1.receive({foo:data, topic:"bar"}); - }); - }); - }); - - it('should retrieve list contents as text with output as multiple msgs ', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",ret:"text",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - cnt++; - msg.should.have.property('topic', 'bar'); - check_parts(msg, cnt -1, 2); - if (cnt !== 1 && cnt !== 2) { - return false; - } - if (cnt === 1) { - msg.payload.indexOf("Apple").should.be.above(-1); - msg.payload.indexOf("Pear").should.be.above(-1); - } else if (cnt === 2) { - msg.payload.indexOf("Potato").should.be.above(-1); - msg.payload.indexOf("Parsnip").should.be.above(-1); - done(); - } - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve an attribute from a tag', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"attr",tag:"span img",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.have.property('src','foo.png'); - msg.should.have.property('topic', 'bar'); - check_parts(msg, 0, 1); - cnt = 2; // frig the answer as only one img tag - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should not reuse message', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",ret:"text",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var prev_msg = undefined; - n2.on("input", function(msg) { - try { - cnt++; - if (prev_msg == undefined) { - prev_msg = msg; - } - else { - msg.should.not.equal(prev_msg); - } - if (cnt == 2) { - done(); - } - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - }); - -}); diff --git a/test/nodes/core/parsers/70-JSON_spec.js b/test/nodes/core/parsers/70-JSON_spec.js deleted file mode 100644 index ad469bbd5..000000000 --- a/test/nodes/core/parsers/70-JSON_spec.js +++ /dev/null @@ -1,546 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var jsonNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-JSON.js"); -var helper = require("node-red-node-test-helper"); - -describe('JSON node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should convert a valid json string to a javascript object', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees[0].should.have.property('firstName', 'John'); - msg.payload.employees[0].should.have.property('lastName', 'Smith'); - done(); - }); - var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; - jn1.receive({payload:jsonString,topic: "bar"}); - }); - }); - - it('should convert a javascript object to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); - done(); - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a array to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '[1,2,3]'); - done(); - }); - var obj = [1,2,3]; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a boolean to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, 'true'); - done(); - }); - var obj = true; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a json string to a boolean', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, true); - done(); - }); - var obj = "true"; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a number to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '2019'); - done(); - }); - var obj = 2019; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a json string to a number', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, 1962); - done(); - }); - var obj = '1962'; - jn1.receive({payload:obj}); - }); - }); - - it('should log an error if asked to parse an invalid json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn1.receive({payload:'foo',topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("Unexpected token o"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },20); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if asked to parse something thats not json or js', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.eql('json.errors.dropped-object'); - done(); - } catch(err) { - done(err); - } - },50); - jn1.receive({payload:Buffer.from("a")}); - }); - }); - - it('should pass straight through if no payload set', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - }); - jn1.receive({topic: "bar"}); - }); - }); - - it('should ensure the result is a json string', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var count = 0; - jn2.on("input", function(msg) { - try { - should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); - count++; - if (count === 2) { - done(); - } - } catch(err) { - done(err); - } - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - jn1.receive({payload:obj,topic: "bar"}); - jn1.receive({payload:JSON.stringify(obj),topic: "bar"}); - }); - }); - - it('should ensure the result is a JS Object', function(done) { - var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var count = 0; - jn2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees[0].should.have.property('firstName', 'John'); - msg.payload.employees[0].should.have.property('lastName', 'Smith'); - count++; - if (count === 2) { - done(); - } - } catch(err) { - done(err); - } - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - jn1.receive({payload:obj,topic: "bar"}); - jn1.receive({payload:JSON.stringify(obj),topic: "bar"}); - }); - }); - - it('should handle any msg property - receive existing string', function(done) { - var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('one'); - msg.one.should.have.property('two'); - msg.one.two.should.have.property('employees'); - msg.one.two.employees[0].should.have.property('firstName', 'John'); - msg.one.two.employees[0].should.have.property('lastName', 'Smith'); - done(); - } catch(err) { - done(err); - } - }); - var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; - jn1.receive({payload:"",one:{two:jsonString},topic: "bar"}); - - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - }); - }); - - it('should handle any msg property - receive existing obj', function(done) { - var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - try { - should.equal(msg.one.two, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); - done(); - } catch(err) { - done(err); - } - }); - var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; - jn1.receive({payload:"",one:{two:JSON.parse(jsonString)},topic: "bar"}); - - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - }); - }); - - it('should pass an object if provided a valid JSON string and schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload.number, 3); - should.equal(msg.payload.string, "allo"); - done(); - }); - var jsonString = '{"number": 3, "string": "allo"}'; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonString, schema:schema}); - }); - }); - - it('should pass an object if provided a valid object and schema and action is object', function(done) { - var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload.number, 3); - should.equal(msg.payload.string, "allo"); - done(); - }); - var obj = {"number": 3, "string": "allo"}; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:obj, schema:schema}); - }); - }); - - it('should pass a string if provided a valid object and schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '{"number":3,"string":"allo"}'); - done(); - }); - var obj = {"number": 3, "string": "allo"}; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:obj, schema:schema}); - }); - }); - - it('should pass a string if provided a valid JSON string and schema and action is string', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '{"number":3,"string":"allo"}'); - done(); - }); - var jsonString = '{"number":3,"string":"allo"}'; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonString, schema:schema}); - }); - }); - - it('should log an error if passed an invalid object and valid schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var obj = {"number": "foo", "string": 3}; - jn1.receive({payload:obj, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed an invalid object and valid schema and action is object', function(done) { - var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var obj = {"number": "foo", "string": 3}; - jn1.receive({payload:obj, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed an invalid JSON string and valid schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var jsonString = '{"number":"Hello","string":3}'; - jn1.receive({payload:jsonString, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed an invalid JSON string and valid schema and action is string', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var jsonString = '{"number":"Hello","string":3}'; - jn1.receive({payload:jsonString, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed a valid object and invalid schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = "garbage"; - var obj = {"number": "foo", "string": 3}; - jn1.receive({payload:obj, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.equal("json.errors.schema-error-compile"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('msg.schema property should be deleted before sending to next node (string input)', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.schema, undefined); - done(); - }); - var jsonString = '{"number":3,"string":"allo"}'; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonString, schema:schema}); - }); - }); - - it('msg.schema property should be deleted before sending to next node (object input)', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.schema, undefined); - done(); - }); - var jsonObject = {"number":3,"string":"allo"}; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonObject, schema:schema}); - }); - }); -}); diff --git a/test/nodes/core/parsers/70-XML_spec.js b/test/nodes/core/parsers/70-XML_spec.js deleted file mode 100644 index e8b281855..000000000 --- a/test/nodes/core/parsers/70-XML_spec.js +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var xmlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-XML.js"); -var helper = require("node-red-node-test-helper"); - -describe('XML node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"xmlNode1", type:"xml", name: "xmlNode" }]; - helper.load(xmlNode, flow, function() { - var xmlNode1 = helper.getNode("xmlNode1"); - xmlNode1.should.have.property('name', 'xmlNode'); - done(); - }); - }); - - it('should convert a valid xml string to a javascript object', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees.should.have.property('firstName'); - should.equal(msg.payload.employees.firstName[0], 'John'); - msg.payload.employees.should.have.property('lastName'); - should.equal(msg.payload.employees.lastName[0], 'Smith'); - done(); - }); - var string = 'JohnSmith'; - n1.receive({payload:string,topic: "bar"}); - }); - }); - - it('should convert a valid xml string to a javascript object - alternative property', function(done) { - var flow = [{id:"n1",type:"xml",property:"foo",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.foo.should.have.property('employees'); - msg.foo.employees.should.have.property('firstName'); - should.equal(msg.foo.employees.firstName[0], 'John'); - msg.foo.employees.should.have.property('lastName'); - should.equal(msg.foo.employees.lastName[0], 'Smith'); - done(); - }); - var string = 'JohnSmith'; - n1.receive({foo:string,topic: "bar"}); - }); - }); - - it('should convert a valid xml string to a javascript object with options', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees.should.have.property('firstName'); - should.equal(msg.payload.employees.firstName[0], 'John'); - msg.payload.employees.should.have.property('lastName'); - should.equal(msg.payload.employees.lastName[0], 'Smith'); - done(); - }); - var string = 'JohnSmith'; - n1.receive({payload:string, topic:"bar", options:{trim:true}}); - }); - }); - - it('should convert a javascript object to an xml string', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - var index = msg.payload.indexOf('JohnSmith'); - index.should.be.above(-1); - done(); - }); - var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }}; - n1.receive({payload:obj,topic: "bar"}); - }); - }); - - it('should convert a javascript object to an xml string with options - alternative property', function(done) { - var flow = [{id:"n1",type:"xml",property:"foo",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - var index = msg.foo.indexOf('\n John\n Smith\n'); - index.should.be.above(-1); - done(); - }); - var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }}; - n1.receive({foo:obj, topic:"bar", options:{headless:true}}); - }); - }); - - it('should log an error if asked to parse an invalid xml string', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:'',topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "xml"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Error: Attribute without value"); - done(); - } catch(err) { - done(err); - } - },200); - }); - }); - - it('should log an error if asked to parse something thats not xml or js', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:1,topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "xml"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg',"xml.errors.xml_js"); - done(); - } catch(err) { - done(err); - } - },200); - }); - }); - - it('should just pass through if payload is missing', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - }); - n1.receive({topic: "bar"}); - }); - }); - -}); diff --git a/test/nodes/core/parsers/70-YAML_spec.js b/test/nodes/core/parsers/70-YAML_spec.js deleted file mode 100644 index 3441e0946..000000000 --- a/test/nodes/core/parsers/70-YAML_spec.js +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var yamlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-YAML.js"); -var helper = require("node-red-node-test-helper"); - -describe('YAML node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"yamlNode1", type:"yaml", name: "yamlNode" }]; - helper.load(yamlNode, flow, function() { - var yamlNode1 = helper.getNode("yamlNode1"); - yamlNode1.should.have.property('name', 'yamlNode'); - done(); - }); - }); - - it('should convert a valid yaml string to a javascript object', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees[0].should.have.property('firstName', 'John'); - msg.payload.employees[0].should.have.property('lastName', 'Smith'); - done(); - }); - var yamlString = "employees:\n - firstName: John\n lastName: Smith\n"; - yn1.receive({payload:yamlString,topic: "bar"}); - }); - }); - - it('should convert a valid yaml string to a javascript object - using another property', function(done) { - var flow = [{id:"yn1",type:"yaml",property:"foo",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.foo.should.have.property('employees'); - msg.foo.employees[0].should.have.property('firstName', 'John'); - msg.foo.employees[0].should.have.property('lastName', 'Smith'); - done(); - }); - var yamlString = "employees:\n - firstName: John\n lastName: Smith\n"; - yn1.receive({foo:yamlString,topic: "bar"}); - }); - }); - - it('should convert a javascript object to a yaml string', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - should.equal(msg.payload, "employees:\n - firstName: John\n lastName: Smith\n"); - done(); - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - yn1.receive({payload:obj}); - }); - }); - - it('should convert a javascript object to a yaml string - using another property', function(done) { - var flow = [{id:"yn1",type:"yaml",property:"foo",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - should.equal(msg.foo, "employees:\n - firstName: John\n lastName: Smith\n"); - done(); - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - yn1.receive({foo:obj}); - }); - }); - - it('should convert an array to a yaml string', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - should.equal(msg.payload, "- 1\n- 2\n- 3\n"); - done(); - }); - var obj = [1,2,3]; - yn1.receive({payload:obj}); - }); - }); - - it('should log an error if asked to parse an invalid yaml string', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - try { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn1.receive({payload:'employees:\n-firstName: John\n- lastName: Smith\n',topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "yaml"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("end of the stream"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if asked to parse something thats not yaml or js', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "yaml"; - }); - logEvents.should.have.length(3); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.eql('yaml.errors.dropped'); - logEvents[1][0].should.have.a.property('msg'); - logEvents[1][0].msg.toString().should.eql('yaml.errors.dropped'); - logEvents[2][0].should.have.a.property('msg'); - logEvents[2][0].msg.toString().should.eql('yaml.errors.dropped-object'); - done(); - } catch(err) { - done(err); - } - },150); - yn1.receive({payload:true}); - yn1.receive({payload:1}); - yn1.receive({payload:Buffer.from("a")}); - }); - }); - - it('should pass straight through if no payload set', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - }); - yn1.receive({topic: "bar"}); - }); - }); - -}); diff --git a/test/nodes/core/sequence/17-split_spec.js b/test/nodes/core/sequence/17-split_spec.js deleted file mode 100644 index 370e0cda4..000000000 --- a/test/nodes/core/sequence/17-split_spec.js +++ /dev/null @@ -1,1919 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var splitNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/17-split.js"); -var joinNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/17-split.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); - -var TimeoutForErrorCase = 20; - -describe('SPLIT node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"splitNode1", type:"split", name:"splitNode" }]; - helper.load(splitNode, flow, function() { - var splitNode1 = helper.getNode("splitNode1"); - splitNode1.should.have.property('name', 'splitNode'); - done(); - }); - }); - - it('should split an array into multiple messages', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("type","array"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.should.equal(1); } - if (msg.parts.index === 1) { msg.payload.should.equal(2); } - if (msg.parts.index === 2) { msg.payload.should.equal(3); } - if (msg.parts.index === 3) { msg.payload.should.equal(4); done(); } - }); - sn1.receive({payload:[1,2,3,4]}); - }); - }); - - it('should split an array into multiple messages of a specified size', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], arraySplt:3, arraySpltType:"len"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",2); - msg.parts.should.have.property("type","array"); - msg.parts.should.have.property("index"); - msg.payload.should.be.an.Array(); - if (msg.parts.index === 0) { msg.payload.length.should.equal(3); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - sn1.receive({payload:[1,2,3,4]}); - }); - }); - - it('should split an object into pieces', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - var count = 0; - sn2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("type","object"); - msg.parts.should.have.property("key"); - msg.parts.should.have.property("count"); - msg.parts.should.have.property("index"); - msg.topic.should.equal("foo"); - if (msg.parts.index === 0) { msg.payload.should.equal(1); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); } - if (msg.parts.index === 2) { msg.payload.should.equal(true); done(); } - }); - sn1.receive({topic:"foo",payload:{a:1,b:"2",c:true}}); - }); - }); - - it('should split an object into pieces and overwrite their topics', function(done) { - var flow = [{id:"sn1", type:"split", addname:"topic", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - var count = 0; - sn2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("type","object"); - msg.parts.should.have.property("key"); - msg.parts.should.have.property("count"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.should.equal(1); msg.topic.should.equal("a"); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); msg.topic.should.equal("b"); } - if (msg.parts.index === 2) { msg.payload.should.equal(true); msg.topic.should.equal("c"); done(); } - }); - sn1.receive({topic:"foo",payload:{a:1,b:"2",c:true}}); - }); - }); - - it('should split a string into new-lines', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("type","string"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.should.equal("Da"); } - if (msg.parts.index === 1) { msg.payload.should.equal("ve"); } - if (msg.parts.index === 2) { msg.payload.should.equal(" "); } - if (msg.parts.index === 3) { msg.payload.should.equal("CJ"); done(); } - }); - sn1.receive({payload:"Da\nve\n \nCJ"}); - }); - }); - - it('should split a string on a specified char', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"\n"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",3); - msg.parts.should.have.property("ch","\n"); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","string"); - if (msg.parts.index === 0) { msg.payload.should.equal("1"); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); } - if (msg.parts.index === 2) { msg.payload.should.equal("3"); done(); } - }); - sn1.receive({payload:"1\n2\n3"}); - }); - }); - - it('should split a string into lengths', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"2", spltType:"len"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("ch",""); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","string"); - if (msg.parts.index === 0) { msg.payload.should.equal("12"); } - if (msg.parts.index === 1) { msg.payload.should.equal("34"); } - if (msg.parts.index === 2) { msg.payload.should.equal("56"); } - if (msg.parts.index === 3) { msg.payload.should.equal("78"); done(); } - }); - sn1.receive({payload:"12345678"}); - }); - }); - - it('should split a string on a specified char in stream mode', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"\n", stream:true}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("ch","\n"); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","string"); - if (msg.parts.index === 0) { msg.payload.should.equal("1"); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); } - if (msg.parts.index === 2) { msg.payload.should.equal("3"); } - if (msg.parts.index === 3) { msg.payload.should.equal("4"); } - if (msg.parts.index === 4) { msg.payload.should.equal("5"); } - if (msg.parts.index === 5) { msg.payload.should.equal("6"); done(); } - }); - sn1.receive({payload:"1\n2\n3\n"}); - sn1.receive({payload:"4\n5\n6\n"}); - }); - }); - - it('should split a buffer into lengths', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"2", spltType:"len"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - try { - //console.log(msg); - msg.should.have.property("parts"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","buffer"); - if (msg.parts.index === 0) { msg.payload.toString().should.equal("12"); } - if (msg.parts.index === 1) { msg.payload.toString().should.equal("34"); } - if (msg.parts.index === 2) { msg.payload.toString().should.equal("56"); } - if (msg.parts.index === 3) { msg.payload.toString().should.equal("78"); done(); } - } catch(err) { - done(err); - } - }); - var b = Buffer.from("12345678"); - sn1.receive({payload:b}); - }); - }); - - it('should split a buffer on another buffer (streaming)', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"[52]", spltType:"bin", stream:true}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - try { - msg.should.have.property("parts"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","buffer"); - if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); } - if (msg.parts.index === 1) { msg.payload.toString().should.equal("123"); } - if (msg.parts.index === 2) { msg.payload.toString().should.equal("123"); done(); } - } catch(err) { - done(err); - } - }); - var b1 = Buffer.from("123412"); - var b2 = Buffer.from("341234"); - sn1.receive({payload:b1}); - sn1.receive({payload:b2}); - }); - }); - - it('should handle invalid spltType (not an array)', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "1", spltType: "bin", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - sn2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should handle invalid splt length', function (done) { - var flow = [{ id: "sn1", type: "split", splt: 0, spltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - sn2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should handle invalid array splt length', function (done) { - var flow = [{ id: "sn1", type: "split", arraySplt: 0, arraySpltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - sn2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should ceil count value when msg.payload type is string', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count", 2); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.length.should.equal(2); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should handle spltBufferString value of undefined', function (done) { - var flow = [{ id: "sn1", type: "split", wires: [["sn2"]], splt: "[52]", spltType: "bin" }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - try { - msg.should.have.property("parts"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); done(); } - } catch (err) { - done(err); - } - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should ceil count value when msg.payload type is Buffer', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count", 2); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.length.should.equal(2); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - var b = Buffer.from("123"); - sn1.receive({ payload: b }); - }); - }); - - it('should set msg.parts.ch when node.spltType is str', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "str", stream: false, wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count", 2); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.length.should.equal(2); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - var b = Buffer.from("123"); - sn1.receive({ payload: b }); - }); - }); - -}); - -describe('JOIN node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function(){ - return Context.clean({allNodes:{}}); - }).then(function(){ - return Context.close(); - }).then(function(){ - RED.settings.nodeMessageBufferMaxLength = 0; - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"joinNode1", type:"join", name:"joinNode" }]; - helper.load(joinNode, flow, function() { - var joinNode1 = helper.getNode("joinNode1"); - joinNode1.should.have.property('name', 'joinNode'); - joinNode1.should.have.property('count', 0); - joinNode1.should.have.property('timer', 0); - joinNode1.should.have.property('build', 'array'); - done(); - }); - }); - - it('should join bits of string back together automatically', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:",", build:"string", mode:"auto"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("A,B,C,D"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:4}}); - n1.receive({payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:4}}); - n1.receive({payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:4}}); - n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}}); - }); - }); - it('should join bits of string back together automatically with a buffer joiner', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:"[44]", joinerType:"bin", build:"string", mode:"auto"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("A,B,C,D"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:4}}); - n1.receive({payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:4}}); - n1.receive({payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:4}}); - n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}}); - }); - }); - - it('should join bits of buffer back together automatically', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:",", build:"buffer", mode:"auto"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.toString().should.equal("A-B-C-D"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:Buffer.from("A"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:0, count:4}}); - n1.receive({payload:Buffer.from("B"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:1, count:4}}); - n1.receive({payload:Buffer.from("C"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:2, count:4}}); - n1.receive({payload:Buffer.from("D"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:3, count:4}}); - }); - }); - - it('should join things into an array after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(true); - //msg.payload[2].a.should.equal(1); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:1}); - n1.receive({payload:true}); - n1.receive({payload:{a:1}}); - }); - }); - it('should join things into an array ignoring msg.parts.index in manual mode', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(true); - //msg.payload[2].a.should.equal(1); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:1, parts: {index: 3}}); - n1.receive({payload:true, parts: {index: 0}}); - n1.receive({payload:{a:1}, parts: {index: 9}}); - }); - }); - - it('should join things into an array after a count with a buffer join set', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joinerType:"bin", joiner:"" ,mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(true); - //msg.payload[2].a.should.equal(1); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:1}); - n1.receive({payload:true}); - n1.receive({payload:{a:1}}); - }); - }); - - it('should join strings into a buffer after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:2, build:"buffer", joinerType:"bin", joiner:"", mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.length.should.equal(10); - msg.payload.toString().should.equal("helloworld"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:"hello"}); - n1.receive({payload:"world"}); - }); - }); - - it('should join things into an object after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:5, build:"object", mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b","2"); - msg.payload.should.have.property("c",true); - msg.payload.should.have.property("d"); - msg.payload.d.should.have.property("e",7); - // msg.payload.should.have.property("g"); - // msg.payload.g.should.have.property("f",6); - done(); - } - catch(e) { done(e)} - }); - n1.receive({payload:1, topic:"a"}); - n1.receive({payload:"2", topic:"b"}); - n1.receive({payload:true, topic:"c"}); - n1.receive({payload:{e:5}, topic:"d"}); - n1.receive({payload:{e:7}, topic:"d"}); - n1.receive({payload:{f:6}, topic:"g"}); - }); - }); - - it('should merge objects', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:5, build:"merged", mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - msg.payload.should.have.property("e",5); - done(); - } - catch(e) { done(e)} - }); - n1.receive({payload:{a:9}, topic:"f"}); - n1.receive({payload:{a:1}, topic:"a"}); - n1.receive({payload:{b:9}, topic:"b"}); - n1.receive({payload:{b:2}, topic:"b"}); - n1.receive({payload:{c:3}, topic:"c"}); - n1.receive({payload:{d:4}, topic:"d"}); - n1.receive({payload:{e:5}, topic:"e"}); - }); - }); - - it('should merge full msg objects', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:6, build:"merged", mode:"custom", propertyType:"full", property:""}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property("payload",7); - msg.payload.should.have.property("aha",'c'); - msg.payload.should.have.property("bar",'b'); - msg.payload.should.have.property("bingo",'e'); - msg.payload.should.have.property("foo",'d'); - msg.payload.should.have.property("topic",'a'); - done(); - } - catch(e) { done(e)} - }); - n1.receive({payload:1, topic:"f"}); - n1.receive({payload:2, topic:"a"}); - n1.receive({payload:3, foo:"b"}); - n1.receive({payload:4, bar:"b"}); - n1.receive({payload:5, aha:"c"}); - n1.receive({payload:6, foo:"d"}); - n1.receive({payload:7, bingo:"e"}); - }); - }); - - it('should accumulate a merged object', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:3}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 3) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",3); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",1); - done(); - } - catch(e) { done(e) } - } - c += 1; - }); - n1.receive({payload:{a:1}, topic:"a"}); - n1.receive({payload:{b:2}, topic:"b"}); - n1.receive({payload:{c:3}, topic:"c"}); - n1.receive({payload:{a:3}, topic:"d"}); - n1.receive({payload:{b:2}, topic:"e"}); - n1.receive({payload:{c:1}, topic:"f"}); - }); - }); - - it('should be able to reset an accumulation', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom", count:3}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 1) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - } - catch(e) { done(e) } - } - if (c === 2) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("e",2); - msg.payload.should.have.property("f",1); - } - catch(e) { done(e) } - } - if (c === 3) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("g",2); - msg.payload.should.have.property("h",1); - msg.payload.should.have.property("i",3); - done(); - } - catch(e) { done(e) } - } - c += 1; - }); - n1.receive({payload:{a:1}, topic:"a"}); - n1.receive({payload:{b:2}, topic:"b"}); - n1.receive({payload:{c:3}, topic:"c"}); - n1.receive({payload:{d:4}, topic:"d", complete:true}); - n1.receive({payload:{e:2}, topic:"e"}); - n1.receive({payload:{f:1}, topic:"f", complete:true}); - n1.receive({payload:{g:2}, topic:"g"}); - n1.receive({payload:{h:1}, topic:"h"}); - n1.receive({reset:true}); - n1.receive({payload:{g:2}, topic:"g"}); - n1.receive({payload:{h:1}, topic:"h"}); - n1.receive({payload:{i:3}, topic:"i"}); - }); - }); - - it('should accumulate a key/value object', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"object", accumulate:true, mode:"custom", topic:"bar", key:"foo", count:4}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - //msg.should.have.property("topic","bar"); - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - done(); - } - catch(e) { done(e) } - }); - n1.receive({payload:2, foo:"b"}); - n1.receive({payload:3, foo:"c"}); - n1.receive({reset:true}); - n1.receive({payload:1, foo:"a"}); - n1.receive({payload:2, foo:"b"}); - n1.receive({payload:3, foo:"c"}); - n1.receive({payload:4, foo:"d"}); - }); - }); - - it('should join strings with a specifed character after a timeout', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:0.05, count:"", joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("a,b,c"); - done(); - } - catch(e) { done(e) } - }); - n1.receive({payload:"a"}); - n1.receive({payload:"b"}); - n1.receive({payload:"c"}); - }); - }); - - it('should allow the timeout to be restarted', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:0.5, count:"", joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("a,b,c"); - const timeTaken = (Date.now() - start)/1000; - // Node times out after 0.5s. - // It receives a restartTimeout after 0.4s. - // So time taken to timeout should be approx 0.9 - timeTaken.should.be.approximately(0.9,0.15); - done(); - } - catch(e) { done(e) } - }); - var start = Date.now(); - n1.receive({payload:"a"}); - setTimeout(function() { - n1.receive({payload:"b", restartTimeout: true}); - n1.receive({payload:"c"}); - },400); - }); - }); - it('should join strings with a specifed character and complete when told to', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:5, count:0, joiner:"\n",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("Hello\nNodeRED\nWorld\n"); - done(); - } - catch(e) { done(e) } - }); - n1.receive({payload:"Hello"}); - n1.receive({payload:"NodeRED"}); - n1.receive({payload:"World"}); - n1.receive({payload:'', complete:true}); - }); - }); - - it('should join complete message objects into an array after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"array", timeout:0, count:3, propertyType:"full",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.be.an.Array(); - msg.payload[0].should.be.an.Object(); - msg.payload[0].should.have.property("payload","a"); - msg.payload[1].should.be.an.Object(); - msg.payload[1].should.have.property("payload","b"); - msg.payload[2].should.be.an.Object(); - msg.payload[2].should.have.property("payload","c"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:"a"}); - n1.receive({payload:"b"}); - n1.receive({payload:"c"}); - }); - }); - - it('should join split things back into an array', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(2); - msg.payload[2].should.equal(3); - msg.payload[3].should.equal(4); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:111}}); - n1.receive({payload:2, parts:{index:1, count:4, id:111}}); - n1.receive({payload:4, parts:{index:3, count:4, id:111}}); - n1.receive({payload:1, parts:{index:0, count:4, id:111}}); - }); - }); - - it('should join split things back into an object', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222, key:"c", type:"object"}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222, key:"b", type:"object"}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222, key:"d", type:"object"}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222, key:"a", type:"object"}}); - }); - }); - - it('should join split things, send when told complete', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:0.250}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - (msg.payload[0] === undefined).should.be.true(); - msg.payload[1].should.equal(2); - msg.payload[2].should.equal(3); - msg.payload[3].should.equal(4); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:444} }); - n1.receive({payload:2, parts:{index:1, count:4, id:444} }); - n1.receive({payload:4, parts:{index:3, count:4, id:444}, complete:true}); - }); - }); - - it('should manually join things into an array, send when told complete', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:1, mode:"custom", build:"array"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload.length.should.equal(3); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(2); - msg.payload[2].should.equal(3); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:1, topic:"A"}); - n1.receive({payload:2, topic:"B"}); - n1.receive({payload:3, topic:"C"}); - n1.receive({complete:true}); - }); - }); - - - it('should manually join things into an object, send when told complete', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:1, mode:"custom", build:"object"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Object(); - Object.keys(msg.payload).length.should.equal(3); - msg.payload.A.should.equal(1); - msg.payload.B.should.equal(2); - msg.payload.C.should.equal(3); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:1, topic:"A"}); - n1.receive({payload:2, topic:"B"}); - n1.receive({payload:3, topic:"C"}); - n1.receive({complete:true}); - }); - }); - - it('should join split strings back into a word', function(done) { - var flow = [{id:"n1", type:"join", mode:"auto", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.String(); - msg.payload.should.equal("abcd"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:"a", parts:{type:'string',index:0, count:4, ch:"", id:555}}); - n1.receive({payload:"d", parts:{type:'string',index:3, count:4, ch:"", id:555}}); - n1.receive({payload:"c", parts:{type:'string',index:2, count:4, ch:"", id:555}}); - n1.receive({payload:"b", parts:{type:'string',index:1, count:4, ch:"", id:555}}); - }); - }); - - it('should allow chained split-split-join-join sequences', function(done) { - var flow = [{id:"s1", type:"split",wires:[["s2"]]}, - {id:"s2", type:"split",wires:[["j1"]]}, - {id:"j1", type:"join", mode:"auto", wires:[["j2"]]}, - {id:"j2", type:"join", mode:"auto", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var s1 = helper.getNode("s1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.eql([[1,2,3],"a\nb\nc",[7,8,9]]); - done(); - } - catch(e) { done(e); } - }); - s1.receive({payload:[[1,2,3],"a\nb\nc",[7,8,9]]}); - }); - }); - - it('should reduce messages', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(10); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages - count only in last part', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(10); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, id:222}}); - n1.receive({payload:2, parts:{index:1, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4,id:222}}); - n1.receive({payload:1, parts:{index:0, id:222}}); - }); - }); - - function checkInitTypes(itype, ival, rval, initializer, checker, done) { - var flow = [{id:"n1", z:"f0", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A", - reduceInit:ival, - reduceInitType:itype, - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - if (!initializer) { - initializer = (node, cb) => { - cb(); - }; - } - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - initializer(n1, function () { - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - checker(msg.payload, rval); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - } - - function checkInitTypesSimple(itype, val, done) { - checkInitTypes(itype, val, val, undefined, should.equal, done); - } - - function checkInitTypesComplex(itype, ival, rval, done) { - checkInitTypes(itype, ival, rval, undefined, should.deepEqual, done); - } - - it('should reduce messages with init types (str)', function(done) { - checkInitTypesSimple('str', "xyz", done); - }); - - it('should reduce messages with init types (num)', function(done) { - checkInitTypesSimple('num', 10, done); - }); - - it('should reduce messages with init types (bool)', function(done) { - checkInitTypesSimple('bool', true, done); - }); - - it('should reduce messages with init types (json)', function(done) { - var ival = '{"x":"vx", "y":"vy", "z":"vz"}'; - var rval = JSON.parse(ival); - checkInitTypesComplex('json', ival, rval, done); - }); - - it('should reduce messages with init types (bin)', function(done) { - var ival = "[1,2,3]"; - var rval = Buffer.from(JSON.parse(ival)); - checkInitTypesComplex('bin', ival, rval, done); - }); - - it('should reduce messages with init types (JSONata)', function(done) { - var ival = "1+2+3"; - var rval = 6; - checkInitTypesComplex('jsonata', ival, rval, done); - }); - - it('should reduce messages with init types (env)', function(done) { - function init(node, cb) { - process.env.NR_XYZ = "nr_xyz"; - cb(); - } - function fin(err) { - delete process.env.NR_XYZ; - done(err); - } - checkInitTypes('env', "NR_XYZ", "nr_xyz", init, should.equal, fin); - }); - - it('should reduce messages with init types (flow.name)', function(done) { - function init(node, cb) { - var context = node.context(); - context.flow.set("foo", "bar"); - cb(); - } - checkInitTypes('flow', "foo", "bar", init, should.equal, done); - }); - - it('should reduce messages with init types (global.name)', function(done) { - function init(node, cb) { - var context = node.context(); - context.global.set("foo", "bar"); - cb(); - } - checkInitTypes('global', "foo", "bar", init, should.equal, done); - }); - - it('should reduce messages using $I', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+$I", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(6); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with fixup', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:"$A/$N", - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(2); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:5, id:222}}); - n1.receive({payload:2, parts:{index:1, count:5, id:222}}); - n1.receive({payload:4, parts:{index:3, count:5, id:222}}); - n1.receive({payload:1, parts:{index:0, count:5, id:222}}); - n1.receive({payload:0, parts:{index:4, count:5, id:222}}); - }); - }); - - it('should reduce messages (left)', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"'(' & $A & '+' & payload & ')'", - reduceInit:"0", - reduceInitType:"str", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.String(); - msg.payload.should.equal("((((0+1)+2)+3)+4)"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:'3', parts:{index:2, count:4, id:222}}); - n1.receive({payload:'2', parts:{index:1, count:4, id:222}}); - n1.receive({payload:'4', parts:{index:3, count:4, id:222}}); - n1.receive({payload:'1', parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages (right)', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:true, - reduceExp:"'(' & $A & '+' & payload & ')'", - reduceInit:"0", - reduceInitType:"str", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.String(); - msg.payload.should.equal("((((0+4)+3)+2)+1)"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:'3', parts:{index:2, count:4, id:222}}); - n1.receive({payload:'2', parts:{index:1, count:4, id:222}}); - n1.receive({payload:'4', parts:{index:3, count:4, id:222}}); - n1.receive({payload:'1', parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with array result', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$append($A,[payload])", - reduceInit:"[]", - reduceInitType:"json", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - var payload = msg.payload; - payload.length.should.equal(2); - if (count == 0) { - payload[0].should.equal(1); - payload[1].should.equal(2); - } - else if (count == 1){ - payload[0].should.equal(3); - payload[1].should.equal(4); - done(); - } - count++; - } - catch(e) { done(e); } - }); - n1.receive({payload:1, parts:{index:0, count:2, id:222}}); - n1.receive({payload:2, parts:{index:1, count:2, id:222}}); - n1.receive({payload:3, parts:{index:2, count:2, id:333}}); - n1.receive({payload:4, parts:{index:3, count:2, id:333}}); - }); - }); - - it('should handle too many pending messages for reduce mode', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "join"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "join"); - evt.should.have.property('msg', "join.too-many"); - done(); - }, TimeoutForErrorCase); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with flow context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload*$flowContext(\"two\"))", - reduceInit:"$flowContext(\"one\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$flowContext(\"three\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1*2)+2*2)+3*2)+4*2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().flow.set("one",1); - n1.context().flow.set("two",2); - n1.context().flow.set("three",3); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with global context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload/$globalContext(\"two\"))", - reduceInit:"$globalContext(\"one\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$globalContext(\"three\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1/2)+2/2)+3/2)+4/2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().global.set("one",1); - n1.context().global.set("two",2); - n1.context().global.set("three",3); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with persistable flow context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload*$flowContext(\"two\",\"memory\"))", - reduceInit:"$flowContext(\"one\",\"memory\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$flowContext(\"three\",\"memory\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - try { - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1*2)+2*2)+3*2)+4*2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().flow.set(["one","two","three"],[1,2,3],"memory",function(err){ - if(err){ - done(err); - } else{ - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - } - }); - }catch(err) { - done(err); - } - }); - }); - }); - - it('should reduce messages with persistable global context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload/$globalContext(\"two\",\"memory\"))", - reduceInit:"$globalContext(\"one\",\"memory\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$globalContext(\"three\",\"memory\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1/2)+2/2)+3/2)+4/2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().global.set(["one","two","three"],[1,2,3],"memory",function(err){ - if(err){ - done(err); - } else{ - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - } - }); - }); - }); - }); - - it('should handle invalid JSONata reduce expression - syntax error"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "invalid expr", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: undefined, - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - - it('should handle invalid JSONata reduce expression - runtime error"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "$uknown()", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: undefined, - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - - it('should handle invalid JSONata fixup expression - syntax err"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "$A", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: "invalid expr", - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - it('should handle invalid JSONata fixup expression - runtime err"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "$A", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: "$unknown()", - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - - it('should concat payload when group.type is array', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], build: "array", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal("ab"); - msg.payload[1].should.equal("cd"); - msg.payload[2].should.equal("ef"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: "ab", parts: { id: 1, type: "array", ch: ",", index: 0, count: 3, len:2}}); - n1.receive({ payload: "cd", parts: { id: 1, type: "array", ch: ",", index: 1, count: 3, len:2}}); - n1.receive({ payload: "ef", parts: { id: 1, type: "array", ch: ",", index: 2, count: 3, len:2}}); - }); - }); - - it('should concat payload when group.type is buffer and group.joinChar is undefined', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: ",", build: "buffer", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.toString().should.equal("ABC"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: Buffer.from("A"), parts: { id: 1, type: "buffer", index: 0, count: 3 } }); - n1.receive({ payload: Buffer.from("B"), parts: { id: 1, type: "buffer", index: 1, count: 3 } }); - n1.receive({ payload: Buffer.from("C"), parts: { id: 1, type: "buffer", index: 2, count: 3 } }); - }); - }); - - it('should concat payload when group.type is string and group.joinChar is not string', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: ",", build: "buffer", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload.toString().should.equal("A0B0C"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: Buffer.from("A"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 0, count: 3 } }); - n1.receive({ payload: Buffer.from("B"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 1, count: 3 } }); - n1.receive({ payload: Buffer.from("C"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 2, count: 3 } }); - }); - }); - - it('should handle msg.parts property when mode is auto and parts or id are missing', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "string", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { type: "string", ch: ",", index: 0, count: 2 } }); - n1.receive({ payload: "B", parts: { type: "string", ch: ",", index: 1, count: 2 } }); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - }); - }); - - it('should handle join an array when mode is auto and duplicate indexed parts arrive', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload[0].should.equal("C"); - msg.payload[1].should.equal("D"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: "A", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "B", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "C", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "D", parts: { id:1, type:"array", ch:",", index:1, count:2 } }); - }); - }); - - it('should handle join an array when using msg.parts and duplicate indexed parts arrive and being reset halfway', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload[0].should.equal("D"); - msg.payload[1].should.equal("C"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: "A", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "B", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ reset:true }); - n1.receive({ payload: "C", parts: { id:1, type:"array", ch:",", index:1, count:2 } }); - n1.receive({ payload: "D", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - }); - }); - - describe('messaging API', function() { - function mapiDoneSplitTestHelper(done, splt, spltType, stream, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - { id: "splitNode1", type:"split", splt, spltType, stream, wires: [[]]}, - { id: "completeNode1", type: "complete", scope: ["splitNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["splitNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([splitNode, completeNode, catchNode], flow, function () { - const splitNode1 = helper.getNode("splitNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 2; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { splitNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when message is sent (string)', function (done) { - mapiDoneSplitTestHelper(done, 2, "len", false, [ - { msg: { seq: 0, payload: "12345" }, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when message is sent (array)', function (done) { - mapiDoneSplitTestHelper(done, 2, "len", false, [ - { msg: { seq: 0, payload: [0,1,2,3,4] }, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when message is sent (object)', function (done) { - mapiDoneSplitTestHelper(done, 2, "len", false, [ - { msg: { seq: 0, payload: {a:1,b:2}}, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when consolidated message is emitted (string, len)', function (done) { - mapiDoneSplitTestHelper(done, 5, "len", true, [ - { msg: { seq: 0, payload: "12"}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: "34"}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: "5"}, delay: 500, avr: 500, var: 100 } - ]); - }); - it('should call done() when consolidated message is emitted (Buffer, len)', function (done) { - mapiDoneSplitTestHelper(done, 5, "len", true, [ - { msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 } - ]); - }); - it('should call done() when consolidated message is emitted (Buffer, str)', function (done) { - mapiDoneSplitTestHelper(done, "5", "str", true, [ - { msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 } - ]); - }); - it('should call done() when consolidated message is emitted (Buffer, bin)', function (done) { - mapiDoneSplitTestHelper(done, "[53]", "bin", true, [ - { msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 } - ]); - }); - - function mapiDoneJoinTestHelper(done, joinNodeSetting, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - { ...joinNodeSetting, id: "joinNode1", type:"join", wires: [[]]}, - { id: "completeNode1", type: "complete", scope: ["joinNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["joinNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([joinNode, completeNode, catchNode], flow, function () { - const joinNode1 = helper.getNode("joinNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 3; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { joinNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when all messages are joined', function (done) { - mapiDoneJoinTestHelper(done, {mode:"auto", timeout:1}, [ - { msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100}, - { msg: {seq:2, payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:3}}, delay:500, avr:500, var:100} - ]); - }); - it('should call done() when the node is reset', function (done) { - mapiDoneJoinTestHelper(done, {mode:"auto", timeout:1}, [ - { msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100}, - { msg: {seq:2, payload:"dummy", reset: true, parts:{id:1}}, delay:500, avr:500, var:100} - ]); - }); - it('should call done() when timed out', function (done) { - mapiDoneJoinTestHelper(done, {mode:"custom", joiner:",", build:"string", timeout:0.5}, [ - { msg: {seq:0, payload:"A"}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:"B"}, delay:200, avr:500, var:100}, - ]); - }); - it('should call done() when all messages are reduced', function (done) { - mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0", - reduceInitType:"num", reduceFixup:undefined}, [ - { msg: {seq:0, payload:3, parts: {index:2, count:3, id:222}}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:2, parts: {index:1, count:3, id:222}}, delay:200, avr:500, var:100}, - { msg: {seq:2, payload:4, parts: {index:0, count:3, id:222}}, delay:500, avr:500, var:100} - ]); - }); - it('should call done() regardless of buffer overflow', function (done) { - mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0", - reduceInitType:"num", reduceFixup:undefined}, [ - { msg: {seq:0, payload:3, parts: {index:2, count:5, id:222}}, delay:0, avr:600, var:100}, - { msg: {seq:1, payload:2, parts: {index:1, count:5, id:222}}, delay:200, avr:600, var:100}, - { msg: {seq:2, payload:4, parts: {index:0, count:5, id:222}}, delay:400, avr:600, var:100}, - { msg: {seq:3, payload:1, parts: {index:3, count:5, id:222}}, delay:600, avr:600, var:100}, - ]); - }); - }); - - it('should handle msg.parts even if messages are out of order in auto mode if exactly one message has count set', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - - n2.on("input", function (msg) { - msg.payload.length.should.be.eql(5); - msg.payload.should.be.eql([0,1,2,3,4]); - done(); - }); - - var msg = {}; - msg.parts = { - id: RED.util.generateId() - }; - for(var elem = 1; elem < 5; ++elem) { - var _msg = RED.util.cloneMessage(msg); - _msg.parts.index = elem; - if(elem == 4) { - _msg.parts.count = 5; - } - _msg.payload = elem; - n1.receive(_msg); - } - - var _msg = RED.util.cloneMessage(msg); - delete _msg.parts.count; - _msg.parts.index = 0; - _msg.payload = 0; - n1.receive(_msg); - }); - - }) - -}); diff --git a/test/nodes/core/sequence/18-sort_spec.js b/test/nodes/core/sequence/18-sort_spec.js deleted file mode 100644 index 955038bfb..000000000 --- a/test/nodes/core/sequence/18-sort_spec.js +++ /dev/null @@ -1,554 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sortNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/18-sort.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); - -describe('SORT node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function(){ - RED.settings.nodeMessageBufferMaxLength = 0; - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, name: "SortNode", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'SortNode'); - done(); - }); - }); - - function check_sort0(flow, target, key, key_type, data_in, data_out, done) { - var sort = flow[0]; - sort.target = target; - sort.targetType = "msg"; - sort.msgKey = key; - sort.msgKeyType = key_type; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property(target); - var data = msg[target]; - data.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - var data0 = data[i]; - var data1 = data_out[i]; - if (typeof data0 === "object") { - data0.should.deepEqual(data1); - } - else { - data0.should.equal(data1); - } - } - done(); - } - catch(e) { - console.log(e); - } - }); - var msg = {}; - msg[target] = data_in; - n1.receive(msg); - }); - } - - function check_sort0A(flow, data_in, data_out, done) { - check_sort0(flow, "payload", "", "elem", data_in, data_out, done); - } - - function check_sort0B(flow, data_in, data_out, done) { - check_sort0(flow, "data", "", "elem", data_in, data_out, done); - } - - function check_sort0C(flow, exp, data_in, data_out, done) { - check_sort0(flow, "data", exp, "jsonata", data_in, data_out, done); - } - - function check_sort1(flow, key, key_type, data_in, data_out, done) { - function equals(v0, v1) { - var k0 = Object.keys(v0); - var k1 = Object.keys(v1); - - if (k0.length === k1.length) { - for (var i = 0; i < k0.length; i++) { - var k = k0[i]; - if (!v1.hasOwnProperty(k) || - (v0[k] !== v1[k])) { - return false; - } - } - return true; - } - return false; - } - function indexOf(a, v) { - for(var i = 0; i < a.length; i++) { - var av = a[i]; - if ((typeof v === 'object') && equals(v, av)) { - return i; - } - else if (v === av) { - return i; - } - } - return -1; - } - var sort = flow[0]; - var prop = (key_type === "msg") ? key : "payload"; - sort.targetType = "seq"; - sort.seqKey = key; - sort.seqKeyType = key_type; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property(prop); - msg.should.have.property("parts"); - msg.parts.should.have.property("count", data_out.length); - var data = msg[prop]; - var index = indexOf(data_out, data); - msg.parts.should.have.property("index", index); - count++; - if (count === data_out.length) { - done(); - } - }); - var len = data_in.length; - for(var i = 0; i < len; i++) { - var parts = { id: "X", index: i, count: len }; - var msg = {parts: parts}; - msg[prop] = data_in[i]; - n1.receive(msg); - } - }); - } - - function check_sort1A(flow, data_in, data_out, done) { - check_sort1(flow, "payload", "msg", data_in, data_out, done); - } - - function check_sort1B(flow, data_in, data_out, done) { - check_sort1(flow, "data", "msg", data_in, data_out, done); - } - - function check_sort1C(flow, exp, data_in, data_out, done) { - check_sort1(flow, exp, "jsonata", data_in, data_out, done); - } - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "1000", "200", "30", "4" ]; - it('should sort payload (elem, not number, ascending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, not number, ascending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (not number, ascending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (not number, ascending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "4", "30", "200", "1000" ]; - it('should sort payload (elem, not number, descending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, not number, descending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (not number, descending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (not number, descending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "4", "30", "200", "1000" ]; - it('should sort payload (elem, number, ascending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, number, ascending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (number, ascending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (number, ascending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "1000", "200", "30", "4" ]; - it('should sort payload (elem, number, descending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, number, descending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (number, descending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (number, descending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "C200", "A4", "B30", "D1000" ]; - var data_out = [ "D1000", "C200", "B30", "A4" ]; - it('should sort payload (exp, not number, ascending)', function(done) { - check_sort0C(flow, "$substring($,1)", data_in, data_out, done); - }); - it('should sort message group (exp, not number, ascending)', function(done) { - check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "C200", "A4", "B30", "D1000" ]; - var data_out = [ "A4", "B30", "C200", "D1000" ]; - it('should sort message group (exp, not number, descending)', function(done) { - check_sort0C(flow, "$substring($,1)", data_in, data_out, done); - }); - it('should sort payload (exp, not number, descending)', function(done) { - check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var conv = function(x) { - return x.map(function(v) { return { val:v }; }); - }; - var data_in = conv([ "200", "4", "30", "1000" ]); - var data_out = conv([ "4", "30", "200", "1000" ]); - it('should sort payload of objects', function(done) { - check_sort0C(flow, "val", data_in, data_out, done); - }); - })(); - - it('should sort payload by context (exp, not number, ascending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$flowContext($)", msgKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "second", "third", "first", "fourth" ]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context()["flow"].set("first","3"); - n1.context()["flow"].set("second","1"); - n1.context()["flow"].set("third","2"); - n1.context()["flow"].set("fourth","4"); - n2.on("input", function(msg) { - msg.should.have.property("data"); - var data = msg["data"]; - data.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - data[i].should.equal(data_out[i]); - } - done(); - }); - var msg = {}; - msg["data"] = data_in; - n1.receive(msg); - }); - }); - - it('should sort message group by context (exp, not number, ascending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"seq", seqKey:"$globalContext(payload)", seqKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "second", "fourth", "third", "first" ]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n1.context()["global"].set("first","4"); - n1.context()["global"].set("second","1"); - n1.context()["global"].set("third","3"); - n1.context()["global"].set("fourth","2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("count", data_out.length); - var data = msg["payload"]; - var index = data_out.indexOf(data); - msg.parts.should.have.property("index", index); - count++; - if (count === data_out.length) { - done(); - } - } - catch(e) { - done(e); - } - }); - var len = data_in.length; - for(var i = 0; i < len; i++) { - var parts = { id: "X", index: i, count: len }; - var msg = {parts: parts}; - msg["payload"] = data_in[i]; - n1.receive(msg); - } - }); - }); - - it('should sort payload by persistable context (exp, not number, descending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$globalContext($,\"memory\")", msgKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "fourth", "first", "third", "second" ]; - helper.load(sortNode, flow, function() { - initContext(function(){ - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context()["global"].set(["first","second","third","fourth"],["3","1","2","4"],"memory",function(){ - n2.on("input", function(msg) { - msg.should.have.property("data"); - var data = msg["data"]; - data.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - data[i].should.equal(data_out[i]); - } - done(); - }); - var msg = {}; - msg["data"] = data_in; - n1.receive(msg); - }); - }); - }); - }); - - it('should sort message group by persistable context (exp, not number, descending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"seq", seqKey:"$flowContext(payload,\"memory\")", seqKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "first", "third", "fourth", "second" ]; - helper.load(sortNode, flow, function() { - initContext(function(){ - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n1.context()["flow"].set(["first","second","third","fourth"],["4","1","3","2"],"memory",function(){ - n2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("count", data_out.length); - var data = msg["payload"]; - var index = data_out.indexOf(data); - msg.parts.should.have.property("index", index); - count++; - if (count === data_out.length) { - done(); - } - }); - var len = data_in.length; - for(var i = 0; i < len; i++) { - var parts = { id: "X", index: i, count: len }; - var msg = {parts: parts}; - msg["payload"] = data_in[i]; - n1.receive(msg); - } - }); - }); - }); - }); - - it('should handle JSONata script error', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"$unknown()", seqKeyType:"jsonata", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "sort"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "sort"); - evt.should.have.property('msg', "sort.invalid-exp"); - done(); - }, 150); - var msg0 = { payload: "A", parts: { id: "X", index: 0, count: 2} }; - var msg1 = { payload: "B", parts: { id: "X", index: 1, count: 2} }; - n1.receive(msg0); - n1.receive(msg1); - }); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "sort"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "sort"); - evt.should.have.property('msg', "sort.too-many"); - done(); - }, 150); - for(var i = 0; i < 4; i++) { - var msg = { payload: "V"+i, - parts: { id: "X", index: i, count: 4} }; - n1.receive(msg); - } - }); - }); - - it('should clear pending messages on close', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var msg = { payload: 0, - parts: { id: "X", index: 0, count: 2} }; - n1.receive(msg); - setTimeout(function() { - n1.close().then(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "sort"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "sort"); - evt.should.have.property('msg', "sort.clear"); - done(); - }); - }, 150); - }); - }); - - describe('messaging API', function() { - function mapiDoneTestHelper(done, targetType, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - {id: "sortNode1", type: "sort", order: "ascending", as_num: false, target: "payload", targetType, - seqKey: "payload", seqKeyType: "msg", wires: [[]]}, - { id: "completeNode1", type: "complete", scope: ["sortNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["sortNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([sortNode, completeNode, catchNode], flow, function () { - const sortNode1 = helper.getNode("sortNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 2; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { sortNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when message is sent (payload)', function (done) { - mapiDoneTestHelper(done, "msg", [ - { msg: { seq: 0, payload: [1, 3, 2] }, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when message is sent (sequence)', function (done) { - mapiDoneTestHelper(done, "seq", [ - { msg: { seq: 0, payload: 3, parts: {id:"A", index: 0, count: 2}}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: 2, parts: {id:"A", index: 1, count: 2}}, delay: 500, avr: 500, var: 100} - ]); - }); - it('should call done() regardless of buffer overflow (same group)', function (done) { - mapiDoneTestHelper(done, "seq", [ - { msg: { seq: 0, payload: 1, parts: {id:"A", index: 0, count: 3}}, delay: 0, avr: 1000, var: 100 }, - { msg: { seq: 1, payload: 3, parts: {id:"A", index: 1, count: 3}}, delay: 500, avr: 1000, var: 100 }, - { msg: { seq: 2, payload: 2, parts: {id:"A", index: 2, count: 3}}, delay: 1000, avr: 1000, var: 100 }, - ]); - }); - it('should call done() regardless of buffer overflow (different group)', function (done) { - mapiDoneTestHelper(done, "seq", [ - { msg: { seq: 0, payload: 1, parts: {id:"A", index: 0, count: 2}}, delay: 0, avr: 1000, var: 100 }, - { msg: { seq: 1, payload: 3, parts: {id:"B", index: 0, count: 2}}, delay: 500, avr: 1200, var: 100 }, - { msg: { seq: 2, payload: 5, parts: {id:"C", index: 0, count: 2}}, delay: 1000, avr: 1500, var: 100 }, - { msg: { seq: 3, payload: 2, parts: {id:"B", index: 1, count: 2}}, delay: 1200, avr: 1200, var: 100 }, - { msg: { seq: 4, payload: 4, parts: {id:"C", index: 1, count: 2}}, delay: 1500, avr: 1500, var: 100 }, - ]); - }); - }); -}); diff --git a/test/nodes/core/sequence/19-batch_spec.js b/test/nodes/core/sequence/19-batch_spec.js deleted file mode 100644 index 2ebcb8d4d..000000000 --- a/test/nodes/core/sequence/19-batch_spec.js +++ /dev/null @@ -1,542 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var batchNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/19-batch.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); - -describe('BATCH node', function() { - this.timeout(8000); - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - RED.settings.nodeMessageBufferMaxLength = 0; - }); - - it('should be loaded with defaults', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'BatchNode'); - done(); - }); - }); - - function check_parts(msg, id, idx, count) { - msg.should.have.property("parts"); - var parts = msg.parts; - parts.should.have.property("id", id); - parts.should.have.property("index", idx); - parts.should.have.property("count", count); - } - - function check_data(n1, n2, results, done) { - var id = undefined; - var ix0 = 0; // seq no - var ix1 = 0; // loc. in seq - var seq = undefined; - var msgs = []; - n2.on("input", function(msg) { - try { - for (var i = 0; i < msgs.length; i++) { - msg.should.not.equal(msgs[i]); - } - msgs.push(msg); - if (seq === undefined) { - seq = results[ix0]; - } - var val = seq[ix1]; - msg.should.have.property("payload", val); - if (id === undefined) { - id = msg.parts.id; - } - check_parts(msg, id, ix1, seq.length); - ix1++; - if (ix1 === seq.length) { - ix0++; - ix1 = 0; - seq = undefined; - id = undefined; - if (ix0 === results.length) { - done(); - } - } - } - catch (e) { - done(e); - } - }); - } - - function check_count(flow, results, done) { - try { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - check_data(n1, n2, results, done); - for(var i = 0; i < 6; i++) { - n1.receive({payload: i}); - } - }); - } - catch (e) { - done(e); - } - } - - function delayed_send(receiver, index, count, delay, done) { - if (index < count) { - setTimeout(function() { - receiver.receive({payload: index}); - delayed_send(receiver, index+1, count, delay, done); - }, delay); - } - else if(index === count) { - if (done) { - done(); - } - } - } - - function check_interval(flow, results, delay, done) { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - check_data(n1, n2, results, done); - delayed_send(n1, 0, 4, delay); - }); - } - - function check_concat(flow, results, inputs, done) { - try { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - check_data(n1, n2, results, done); - for(var data of inputs) { - var msg = { - topic: data[0], - payload: data[1], - parts: { - id: data[0], - index: data[2], - count: data[3] - } - }; - n1.receive(msg); - } - }); - } - catch (e) { - done(e); - } - } - - describe('mode: count', function() { - - it('should create seq. with count', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 2, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1], - [2, 3], - [4, 5] - ]; - check_count(flow, results, done); - }); - - it('should create seq. with count and overlap', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 3, overlap: 2, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1, 2], - [1, 2, 3], - [2, 3, 4], - [3, 4, 5] - ]; - check_count(flow, results, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 5, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "batch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "batch"); - evt.should.have.property('msg', "batch.too-many"); - done(); - }, 150); - for(var i = 0; i < 3; i++) { - n1.receive({payload: i}); - } - }); - }); - - it('should handle reset', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 2, overlap: 0, interval: 0, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var results = [ - [0, 1], - [4, 5] - ]; - check_data(n1, n2, results, done); - n1.receive({payload:0}); - n1.receive({payload:1}); - n1.receive({payload:2}); - n1.receive({payload:3, reset: true}); - n1.receive({payload:4}); - n1.receive({payload:5}); - }); - }); - }); - - describe('mode: interval', function() { - it('should create seq. with interval', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1], - [2, 3] - ]; - check_interval(flow, results, 450, done); - }); - - it('should create seq. with interval (in float)', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 0.5, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1], - [2, 3] - ]; - check_interval(flow, results, 225, done); - }); - - it('should create seq. with interval & not send empty seq', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - // 1300, 2600, 3900, 5200, - [0], [1], [2], [3] - ]; - check_interval(flow, results, 1300, done); - }); - - it('should create seq. with interval & send empty seq', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: true, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - // 1300, 2600, 3900, 5200, - [null], [0], [1], [2], [null], [3] - ]; - check_interval(flow, results, 1300, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "batch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "batch"); - evt.should.have.property('msg', "batch.too-many"); - done(); - }, 150); - for(var i = 0; i < 3; i++) { - n1.receive({payload: i}); - } - }); - }); - - it('should handle reset', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var results = [ - [0, 1], - [4, 5] - ]; - check_data(n1, n2, results, done); - delayed_send(n1, 0, 3, 400, function () { - setTimeout(function () { - n1.receive({payload: "3", reset: true}); - delayed_send(n1, 4, 7, 400); - }, 10); - }); - }); - }); - - }); - - describe('mode: concat', function() { - it('should concat two seq. (series)', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [2, 3, 0, 1] - ]; - var inputs = [ - ["TB", 0, 0, 2], - ["TB", 1, 1, 2], - ["TA", 2, 0, 2], - ["TA", 3, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should concat two seq. (mixed)', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [2, 3, 0, 1] - ]; - var inputs = [ - ["TA", 2, 0, 2], - ["TB", 0, 0, 2], - ["TA", 3, 1, 2], - ["TB", 1, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should concat three seq.', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}, {topic: "TC"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [2, 3, 0, 1, 4] - ]; - var inputs = [ - ["TC", 4, 0, 1], - ["TB", 0, 0, 2], - ["TB", 1, 1, 2], - ["TA", 2, 0, 2], - ["TA", 3, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should concat same seq.', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TA"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [9, 8, 9, 8] - ]; - var inputs = [ - ["TA", 9, 0, 2], - ["TA", 8, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "batch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "batch"); - evt.should.have.property('msg', "batch.too-many"); - done(); - }, 150); - var C = 3; - for(var i = 0; i < C; i++) { - var parts_a = {index:i, count:C, id:"A"}; - var parts_b = {index:i, count:C, id:"B"}; - n1.receive({payload: i, topic: "TA", parts:parts_a}); - n1.receive({payload: i, topic: "TB", parts:parts_b}); - } - }); - }); - - it('should handle reset', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - try { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var results = [ - [2, 3, 0, 1] - ]; - check_data(n1, n2, results, done); - var inputs0 = [ - ["TB", 0, 0, 2], - ["TA", 1, 0, 2], - ]; - for(var data of inputs0) { - var msg = { - topic: data[0], - payload: data[1], - parts: { - id: data[0], - index: data[2], - count: data[3] - } - }; - n1.receive(msg); - } - n1.receive({payload: undefined, reset: true}); - var inputs1 = [ - ["TB", 0, 0, 2], - ["TB", 1, 1, 2], - ["TA", 2, 0, 2], - ["TA", 3, 1, 2] - ]; - for(var data of inputs1) { - var msg = { - topic: data[0], - payload: data[1], - parts: { - id: data[0], - index: data[2], - count: data[3] - } - }; - n1.receive(msg); - } - }); - } - catch (e) { - done(e); - } - }); - }); - - describe('messaging API', function() { - function mapiDoneTestHelper(done, mode, count, overlap, interval, allowEmptySequence, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [{id:"batchNode1", type:"batch", name: "BatchNode", mode, count, overlap, interval, - allowEmptySequence, topics: [{topic: "TA"}], wires:[[]]}, - {id:"completeNode1",type:"complete",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]}, - {id:"catchNode1", type:"catch",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]}, - {id:"helperNode1",type:"helper", wires:[[]]}]; - const numMsgs = msgAndTimings.length; - helper.load([batchNode, completeNode, catchNode], flow, function () { - const batchNode1 = helper.getNode("batchNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 2; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.payload].avr, msgAndTimings[msg.payload].var); - c += 1; - if ( c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout( function() { batchNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - - it('should call done() when message is sent (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 0, var: 100}, - { msg: {payload: 1}, delay: 0, avr: 0, var: 100} - ]); - }); - it('should call done() when reset (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 200, var: 100}, - { msg: {payload: 1, reset:true}, delay: 200, avr: 200, var: 100} - ]); - }); - it('should call done() regardless of buffer overflow (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 10, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 500, var: 100}, - { msg: {payload: 1}, delay: 100, avr: 500, var: 100}, - { msg: {payload: 2}, delay: 500, avr: 500, var: 100} - ]); - }); - it('should call done() when message is sent (mode: interval)', function(done) { - mapiDoneTestHelper(done, "interval", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 2000, var: 100}, - { msg: {payload: 1}, delay: 500, avr: 2000, var: 100} - ]); - }); - it('should call done() when reset (mode: interval)', function(done) { - mapiDoneTestHelper(done, "interval", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 200, var: 100}, - { msg: {payload: 1, reset:true}, delay: 200, avr: 200, var: 100} - ]); - }); - it('should call done() regardless of buffer overflow (mode: interval)', function(done) { - mapiDoneTestHelper(done, "interval", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 500, var: 100}, - { msg: {payload: 1}, delay: 100, avr: 500, var: 100}, - { msg: {payload: 2}, delay: 500, avr: 500, var: 100} - ]); - }); - it('should call done() when message is sent (mode: concat)', function(done) { - mapiDoneTestHelper(done, "concat", 2, 0, 2, false, [ - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 0, count: 2}}, delay: 0, avr: 1000, var: 100}, - { msg: {topic:"TA", payload: 1, parts: {id: "TA", index: 1, count: 2}}, delay: 1000, avr: 1000, var: 100}, - ]); - }); - it('should call done() when reset (mode: concat)', function(done) { - mapiDoneTestHelper(done, "concat", 2, 0, 2, false, [ - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 0, count: 2}}, delay: 0, avr: 1000, var: 100}, - { msg: {payload: 1, reset:true}, delay: 1000, avr: 1000, var: 100}, - ]); - }); - it('should call done() regardless of buffer overflow (mode: concat)', function(done) { - mapiDoneTestHelper(done, "concat", 2, 0, 2, false, [ - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 0, count: 3}}, delay: 0, avr: 1000, var: 100}, - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 1, count: 3}}, delay: 500, avr: 1000, var: 100}, - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 2, count: 3}}, delay: 1000, avr: 1000, var: 100} - ]); - }); - }); -}); diff --git a/test/nodes/core/storage/10-file_spec.js b/test/nodes/core/storage/10-file_spec.js deleted file mode 100644 index 99ed23978..000000000 --- a/test/nodes/core/storage/10-file_spec.js +++ /dev/null @@ -1,1722 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var path = require('path'); -var fs = require('fs-extra'); -var os = require('os'); -var sinon = require("sinon"); -var iconv = require("iconv-lite"); -var fileNode = require("nr-test-utils").require("@node-red/nodes/core/storage/10-file.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); - -describe('file Nodes', function() { - - function encode(s, enc) { - if (enc === "none") { - return Buffer.from(s); - } - return iconv.encode(s, enc); - } - - function decode(data, enc) { - if (enc === "none") { - return data.toString(); - } - return iconv.decode(data, enc); - } - - describe('file out Node', function() { - - var relativePathToFile = "50-file-test-file.txt"; - var resourcesDir = path.join(__dirname,"..","..","..","resources"); - var fileToTest = path.join(resourcesDir,relativePathToFile); - var wait = 250; - - beforeEach(function(done) { - //fs.writeFileSync(fileToTest, "File message line 1\File message line 2\n"); - helper.startServer(done); - }); - - afterEach(function(done) { - delete RED.settings.fileWorkingDirectory; - fs.removeSync(path.join(resourcesDir,"file-out-node")); - helper.unload().then(function() { - //fs.unlinkSync(fileToTest); - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":true}]; - helper.load(fileNode, flow, function() { - try { - var fileNode1 = helper.getNode("fileNode1"); - fileNode1.should.have.property('name', 'fileNode'); - done(); - } - catch (e) { - done(e); - } - }); - }); - - it('should write to a file', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest); - f.should.have.length(4); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", "test"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"test"}); - }); - }); - - it('should write to a file using RED.settings.fileWorkingDirectory', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":relativePathToFile, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - RED.settings.fileWorkingDirectory = resourcesDir; - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest); - f.should.have.length(4); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", "test"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"test"}); - }); - }); - - - it('should write multi-byte string to a file', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.have.length(2); - f.should.equal("試験"); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", "試験"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"試験"}); - }); - }); - - it('should append to a file and add newline', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - try { - fs.unlinkSync(fileToTest); - } catch(err) { - } - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var count = 0; - var data = ["test2", true, 999, [2]]; - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - data.should.containDeep([msg.payload]); - if (count === 3) { - var f = fs.readFileSync(fileToTest).toString(); - if (os.type() !== "Windows_NT") { - f.should.have.length(19); - f.should.equal("test2\ntrue\n999\n[2]\n"); - } - else { - f.should.have.length(23); - f.should.equal("test2\r\ntrue\r\n999\r\n[2]\r\n"); - } - done(); - } - count++; - } - catch (e) { - done(e); - } - }); - - n1.receive({payload:"test2"}); // string - setTimeout(function() { - n1.receive({payload:true}); // boolean - },30); - setTimeout(function() { - n1.receive({payload:999}); // number - },60); - setTimeout(function() { - n1.receive({payload:[2]}); // object (array) - },90); - }); - }); - - it('should append to a file after it has been deleted ', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - try { - fs.unlinkSync(fileToTest); - } catch(err) { - } - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var data = ["one", "two", "three", "four"]; - var count = 0; - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - data.should.containDeep([msg.payload]); - try { - if (count === 1) { - // Check they got appended as expected - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("onetwo"); - - // Delete the file - fs.unlinkSync(fileToTest); - setTimeout(function() { - // Send two more messages to the file - n1.receive({payload:"three"}); - n1.receive({payload:"four"}); - }, wait); - } - if (count === 3) { - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("threefour"); - fs.unlinkSync(fileToTest); - done(); - } - } catch(err) { - done(err); - } - count++; - } - catch (e) { - done(e); - } - }); - - // Send two messages to the file - n1.receive({payload:"one"}); - n1.receive({payload:"two"}); - }); - }); - - it('should append to a file after it has been recreated ', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - try { - fs.unlinkSync(fileToTest); - } catch(err) { - } - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var data = ["one", "two", "three", "four"]; - var count = 0; - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - data.should.containDeep([msg.payload]); - if (count == 1) { - // Check they got appended as expected - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("onetwo"); - - if (os.type() === "Windows_NT") { - var dummyFile = path.join(resourcesDir,"50-file-test-dummy.txt"); - fs.rename(fileToTest, dummyFile, function() { - recreateTest(n1, dummyFile); - }); - } else { - recreateTest(n1, fileToTest); - } - } - if (count == 3) { - // Check the file was updated - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("threefour"); - fs.unlinkSync(fileToTest); - done(); - } catch(err) { - done(err); - } - } - } catch(err) { - done(err); - } - count++; - }); - - // Send two messages to the file - n1.receive({payload:"one"}); - n1.receive({payload:"two"}); - }); - - function recreateTest(n1, fileToDelete) { - // Delete the file - fs.unlinkSync(fileToDelete); - - // Recreate it - fs.writeFileSync(fileToTest,""); - - // Send two more messages to the file - n1.receive({payload:"three"}); - n1.receive({payload:"four"}); - } - }); - - - it('should use msg.filename if filename not set in node', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload", "fine"); - msg.should.have.property("filename", fileToTest); - - var f = fs.readFileSync(fileToTest).toString(); - if (os.type() !== "Windows_NT") { - f.should.have.length(5); - f.should.equal("fine\n"); - } - else { - f.should.have.length(6); - f.should.equal("fine\r\n"); - } - done(); - } - catch (e) { - done(e); - } - }); - - n1.receive({payload:"fine", filename:fileToTest}); - }); - }); - - it('should be able to delete the file', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":"delete", wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - - n2.on("input", function (msg) { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.not.equal("fine"); - //done(); - } - catch(e) { - e.code.should.equal("ENOENT"); - done(); - } - }); - - n1.receive({payload:"fine"}); - }); - }); - - it('should warn if filename not set', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - n1.emit("input", {payload:"nofile"}); - setTimeout(function() { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.not.equal("fine"); - //done(); - } - catch(e) { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.equal("file.errors.nofilename"); - done(); - } - },wait); - }); - }); - - it('ignore a missing payload', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.not.equal("fine"); - //done(); - } - catch(e) { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(0); - done(); - } - },wait); - n1.emit("input", {topic:"test"}); - }); - }); - - it('should fail to write to a ro file', function(done) { - // Stub file write so we can make writes fail - var spy = sinon.stub(fs, 'createWriteStream').callsFake(function(arg1,arg2) { - var ws = {}; - ws.on = function(e,d) { throw("Stub error message"); } - ws.write = function(e,d) { } - return ws; - }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Stub error message"); - done(); - } - catch(e) { done(e); } - finally { fs.createWriteStream.restore(); } - },wait); - n1.receive({payload:"test"}); - }); - }); - - it('should fail to append to a ro file', function(done) { - // Stub file write so we can make writes fail - var spy = sinon.stub(fs, 'createWriteStream').callsFake(function(arg1,arg2) { - var ws = {}; - ws.on = function(e,d) { throw("Stub error message"); } - ws.write = function(e,d) { } - return ws; - }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Stub error message"); - done(); - } - catch(e) { done(e); } - finally { fs.createWriteStream.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should cope with failing to delete a file', function(done) { - // Stub file write so we can make writes fail - var spy = sinon.stub(fs, 'unlink').callsFake(function(arg,arg2) { arg2(new Error("Stub error message")); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":"delete"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("file.errors.deletefail"); - done(); - } - catch(e) { done(e); } - finally { fs.unlink.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should fail to create a new directory if not asked to do so (append)', function(done) { - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - //var spy = sinon.stub(fs, 'appendFile').callsFake(function(arg,arg2,arg3,arg4){ arg4(new Error("Stub error message")); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("file.errors.appendfail"); - done(); - } - catch(e) { done(e); } - //finally { fs.appendFile.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should try to create a new directory if asked to do so (append)', function(done) { - // fs.writeFileSync of afterEach failed on Windows. - if (os.type() === "Windows_NT") { - done(); - return; - } - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - var spy = sinon.stub(fs, "ensureDir").callsFake(function(arg1,arg2,arg3,arg4) { arg2(null); }); - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":false, "createDir":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(0); - done(); - } - catch(e) { done(e); } - finally { fs.ensureDir.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should fail to create a new directory if not asked to do so (overwrite)', function(done) { - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - //var spy = sinon.stub(fs, 'appendFile').callsFake(function(arg,arg2,arg3,arg4){ arg4(new Error("Stub error message")); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":false, "overwriteFile":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("file.errors.writefail"); - done(); - } - catch(e) { done(e); } - //finally { fs.appendFile.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should try to create a new directory if asked to do so (overwrite)', function(done) { - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - var spy = sinon.stub(fs, "ensureDir").callsFake(function(arg1,arg2,arg3,arg4) { arg2(null); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":true, "createDir":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(0); - done(); - } - catch(e) { done(e); } - finally { fs.ensureDir.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should write to multiple files', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, "createDir":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - var tmp_path = path.join(resourcesDir, "tmp"); - var len = 1024*1024*10; - var file_count = 5; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var count = 0; - n2.on("input", function(msg) { - try { - count++; - if (count == file_count) { - for(var i = 0; i < file_count; i++) { - var name = path.join(tmp_path, String(i)); - var f = fs.readFileSync(name); - f.should.have.length(len); - f[0].should.have.equal(i); - } - fs.removeSync(tmp_path); - done(); - } - } - catch (e) { - try { - fs.removeSync(tmp_path); - } - catch (e1) { - } - done(e); - } - }); - for(var i = 0; i < file_count; i++) { - var data = Buffer.alloc?Buffer.alloc(len):new Buffer(len); - data.fill(i); - var name = path.join(tmp_path, String(i)); - var msg = {payload:data, filename:name}; - n1.receive(msg); - } - }); - }); - - it('should write to multiple files if node is closed', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, "createDir":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - var tmp_path = path.join(resourcesDir, "tmp"); - var len = 1024*1024*10; - var file_count = 5; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var count = 0; - n2.on("input", function(msg) { - try { - count++; - if (count == file_count) { - for(var i = 0; i < file_count; i++) { - var name = path.join(tmp_path, String(i)); - var f = fs.readFileSync(name); - f.should.have.length(len); - f[0].should.have.equal(i); - } - fs.removeSync(tmp_path); - done(); - } - } - catch (e) { - try { - fs.removeSync(tmp_path); - } - catch (e1) { - } - done(e); - } - }); - for(var i = 0; i < file_count; i++) { - var data = Buffer.alloc?Buffer.alloc(len):new Buffer(len); - data.fill(i); - var name = path.join(tmp_path, String(i)); - var msg = {payload:data, filename:name}; - n1.receive(msg); - } - n1.close(); - }); - }); - - describe('encodings', function() { - - function checkWriteWithEncoding(enc, data, done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, encoding:enc, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest); - f.equals(encode(data, enc)).should.be.true(); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", data); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:data}); - }); - } - - // default - it('should write to a file with "none" encoding', function(done) { - checkWriteWithEncoding("none", "test", done); - }); - - // Native - it('should write to a file with "utf8" encoding', function(done) { - checkWriteWithEncoding("utf8", "試験", done); - }); - - it('should write to a file with "ucs2" encoding', function(done) { - checkWriteWithEncoding("ucs2", "試験", done); - }); - - it('should write to a file with "utf-16le" encoding', function(done) { - checkWriteWithEncoding("utf-16le", "試験", done); - }); - - it('should write to a file with "binary" encoding', function(done) { - checkWriteWithEncoding("binary", "test", done); - }); - - it('should write to a file with "base64" encoding', function(done) { - checkWriteWithEncoding("base64", "5pel5pys6KqeCg==", done); - }); - - it('should write to a file with "hex" encoding', function(done) { - checkWriteWithEncoding("hex", "deadbeef", done); - }); - - // Unicode - it('should write to a file with "utf-16be" encoding', function(done) { - checkWriteWithEncoding("utf-16be", "試験", done); - }); - - // Japanese - it('should write to a file with "Shift_JIS" encoding', function(done) { - checkWriteWithEncoding("Shift_JIS", "試験", done); - }); - - it('should write to a file with "Windows-31j" encoding', function(done) { - checkWriteWithEncoding("Windows-31j", "試験", done); - }); - - it('should write to a file with "Windows932" encoding', function(done) { - checkWriteWithEncoding("Windows932", "試験", done); - }); - - it('should write to a file with "EUC-JP" encoding', function(done) { - checkWriteWithEncoding("EUC-JP", "試験", done); - }); - - // following encoding tests should be more specific - // Chinese - it('should write to a file with "GB2312" encoding', function(done) { - checkWriteWithEncoding("GB2312", "test", done); - }); - - it('should write to a file with "GBK" encoding', function(done) { - checkWriteWithEncoding("GBK", "test", done); - }); - - it('should write to a file with "GB18030" encoding', function(done) { - checkWriteWithEncoding("GB18030", "test", done); - }); - - it('should write to a file with "Windows936" encoding', function(done) { - checkWriteWithEncoding("Windows936", "test", done); - }); - - it('should write to a file with "EUC-CN" encoding', function(done) { - checkWriteWithEncoding("EUC-CN", "test", done); - }); - - // Korean - it('should write to a file with "KS_C_5601" encoding', function(done) { - checkWriteWithEncoding("KS_C_5601", "test", done); - }); - - it('should write to a file with "Windows949" encoding', function(done) { - checkWriteWithEncoding("Windows949", "test", done); - }); - - it('should write to a file with "EUC-KR" encoding', function(done) { - checkWriteWithEncoding("EUC-KR", "test", done); - }); - - // Taiwan/Hong Kong - it('should write to a file with "Big5" encoding', function(done) { - checkWriteWithEncoding("Big5", "test", done); - }); - - it('should write to a file with "Big5-HKSCS" encoding', function(done) { - checkWriteWithEncoding("Big5-HKSCS", "test", done); - }); - - it('should write to a file with "Windows950" encoding', function(done) { - checkWriteWithEncoding("Windows950", "test", done); - }); - - // Windows - it('should write to a file with "cp874" encoding', function(done) { - checkWriteWithEncoding("cp874", "test", done); - }); - - it('should write to a file with "cp1250" encoding', function(done) { - checkWriteWithEncoding("cp1250", "test", done); - }); - - it('should write to a file with "cp1251" encoding', function(done) { - checkWriteWithEncoding("cp1251", "test", done); - }); - - it('should write to a file with "cp1252" encoding', function(done) { - checkWriteWithEncoding("cp1252", "test", done); - }); - - it('should write to a file with "cp1253" encoding', function(done) { - checkWriteWithEncoding("cp1253", "test", done); - }); - - it('should write to a file with "cp1254" encoding', function(done) { - checkWriteWithEncoding("cp1254", "test", done); - }); - - it('should write to a file with "cp1255" encoding', function(done) { - checkWriteWithEncoding("cp1255", "test", done); - }); - - it('should write to a file with "cp1256" encoding', function(done) { - checkWriteWithEncoding("cp1256", "test", done); - }); - - it('should write to a file with "cp1257" encoding', function(done) { - checkWriteWithEncoding("cp1257", "test", done); - }); - - it('should write to a file with "cp1258" encoding', function(done) { - checkWriteWithEncoding("cp1258", "test", done); - }); - - // IBM - it('should write to a file with "cp437" encoding', function(done) { - checkWriteWithEncoding("cp437", "test", done); - }); - - it('should write to a file with "cp737" encoding', function(done) { - checkWriteWithEncoding("cp737", "test", done); - }); - - it('should write to a file with "cp775" encoding', function(done) { - checkWriteWithEncoding("cp775", "test", done); - }); - - it('should write to a file with "cp808" encoding', function(done) { - checkWriteWithEncoding("cp808", "test", done); - }); - - it('should write to a file with "cp850" encoding', function(done) { - checkWriteWithEncoding("cp850", "test", done); - }); - - it('should write to a file with "cp852" encoding', function(done) { - checkWriteWithEncoding("cp852", "test", done); - }); - - it('should write to a file with "cp855" encoding', function(done) { - checkWriteWithEncoding("cp855", "test", done); - }); - - it('should write to a file with "cp856" encoding', function(done) { - checkWriteWithEncoding("cp856", "test", done); - }); - - it('should write to a file with "cp857" encoding', function(done) { - checkWriteWithEncoding("cp857", "test", done); - }); - - it('should write to a file with "cp858" encoding', function(done) { - checkWriteWithEncoding("cp858", "test", done); - }); - - it('should write to a file with "cp860" encoding', function(done) { - checkWriteWithEncoding("cp860", "test", done); - }); - - it('should write to a file with "cp861" encoding', function(done) { - checkWriteWithEncoding("cp861", "test", done); - }); - - it('should write to a file with "cp866" encoding', function(done) { - checkWriteWithEncoding("cp866", "test", done); - }); - - it('should write to a file with "cp869" encoding', function(done) { - checkWriteWithEncoding("cp869", "test", done); - }); - - it('should write to a file with "cp922" encoding', function(done) { - checkWriteWithEncoding("cp922", "test", done); - }); - - it('should write to a file with "cp1046" encoding', function(done) { - checkWriteWithEncoding("cp1046", "test", done); - }); - - it('should write to a file with "cp1124" encoding', function(done) { - checkWriteWithEncoding("cp1124", "test", done); - }); - - it('should write to a file with "cp1125" encoding', function(done) { - checkWriteWithEncoding("cp1125", "test", done); - }); - - it('should write to a file with "cp1129" encoding', function(done) { - checkWriteWithEncoding("cp1129", "test", done); - }); - - it('should write to a file with "cp1133" encoding', function(done) { - checkWriteWithEncoding("cp1133", "test", done); - }); - - it('should write to a file with "cp1161" encoding', function(done) { - checkWriteWithEncoding("cp1161", "test", done); - }); - - it('should write to a file with "cp1162" encoding', function(done) { - checkWriteWithEncoding("cp1162", "test", done); - }); - - it('should write to a file with "cp1163" encoding', function(done) { - checkWriteWithEncoding("cp1163", "test", done); - }); - - // Mac - it('should write to a file with "maccroatian" encoding', function(done) { - checkWriteWithEncoding("maccroatian", "test", done); - }); - - it('should write to a file with "maccyrillic" encoding', function(done) { - checkWriteWithEncoding("maccyrillic", "test", done); - }); - - it('should write to a file with "macgreek" encoding', function(done) { - checkWriteWithEncoding("macgreek", "test", done); - }); - - it('should write to a file with "maciceland" encoding', function(done) { - checkWriteWithEncoding("maciceland", "test", done); - }); - - it('should write to a file with "macroman" encoding', function(done) { - checkWriteWithEncoding("macroman", "test", done); - }); - - it('should write to a file with "macromania" encoding', function(done) { - checkWriteWithEncoding("macromania", "test", done); - }); - - it('should write to a file with "macthai" encoding', function(done) { - checkWriteWithEncoding("macthai", "test", done); - }); - - it('should write to a file with "macturkish" encoding', function(done) { - checkWriteWithEncoding("macturkish", "test", done); - }); - - it('should write to a file with "macukraine" encoding', function(done) { - checkWriteWithEncoding("macukraine", "test", done); - }); - - it('should write to a file with "maccenteuro" encoding', function(done) { - checkWriteWithEncoding("maccenteuro", "test", done); - }); - - it('should write to a file with "macintosh" encoding', function(done) { - checkWriteWithEncoding("macintosh", "test", done); - }); - - // KOI8 - it('should write to a file with "koi8-r" encoding', function(done) { - checkWriteWithEncoding("koi8-r", "test", done); - }); - - it('should write to a file with "koi8-u" encoding', function(done) { - checkWriteWithEncoding("koi8-u", "test", done); - }); - - it('should write to a file with "koi8-ru" encoding', function(done) { - checkWriteWithEncoding("koi8-ru", "test", done); - }); - - it('should write to a file with "koi8-t" encoding', function(done) { - checkWriteWithEncoding("koi8-t", "test", done); - }); - - // Misc - it('should write to a file with "armscii8" encoding', function(done) { - checkWriteWithEncoding("armscii8", "test", done); - }); - - it('should write to a file with "rk1048" encoding', function(done) { - checkWriteWithEncoding("rk1048", "test", done); - }); - - it('should write to a file with "tcvn" encoding', function(done) { - checkWriteWithEncoding("tcvn", "test", done); - }); - - it('should write to a file with "georgianacademy" encoding', function(done) { - checkWriteWithEncoding("georgianacademy", "test", done); - }); - - it('should write to a file with "georgianps" encoding', function(done) { - checkWriteWithEncoding("georgianps", "test", done); - }); - - it('should write to a file with "pt154" encoding', function(done) { - checkWriteWithEncoding("pt154", "test", done); - }); - - it('should write to a file with "viscii" encoding', function(done) { - checkWriteWithEncoding("viscii", "test", done); - }); - - it('should write to a file with "iso646cn" encoding', function(done) { - checkWriteWithEncoding("iso646cn", "test", done); - }); - - it('should write to a file with "iso646jp" encoding', function(done) { - checkWriteWithEncoding("iso646jp", "test", done); - }); - - it('should write to a file with "hproman8" encoding', function(done) { - checkWriteWithEncoding("hproman8", "test", done); - }); - - it('should write to a file with "tis620" encoding', function(done) { - checkWriteWithEncoding("tis620", "test", done); - }); - - }); - }); - - - describe('file in Node', function() { - - var relativePathToFile = "50-file-test-file.txt"; - var resourcesDir = path.join(__dirname,"..","..","..","resources"); - var fileToTest = path.join(resourcesDir,relativePathToFile); - var fileToTest2 = "\t"+path.join(resourcesDir,relativePathToFile)+"\r\n"; - var wait = 150; - - beforeEach(function(done) { - fs.writeFileSync(fileToTest, "File message line 1\nFile message line 2\n"); - helper.startServer(done); - }); - - afterEach(function(done) { - delete RED.settings.fileWorkingDirectory; - helper.unload().then(function() { - fs.unlinkSync(fileToTest); - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - n1.should.have.property('name', 'fileInNode'); - done(); - }); - }); - - it('should read in a file and output a buffer', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name:"fileInNode", "filename":fileToTest, "format":"", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.should.have.length(40); - msg.payload.toString().should.equal('File message line 1\nFile message line 2\n'); - done(); - }); - n1.receive({payload:""}); - }); - }); - - it('should read in a file and output a utf8 string', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.have.length(40) - msg.payload.should.equal("File message line 1\nFile message line 2\n"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - - it('should read in a file using fileWorkingDirectory to set cwd', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":relativePathToFile, "format":"utf8", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - RED.settings.fileWorkingDirectory = resourcesDir; - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.have.length(40) - msg.payload.should.equal("File message line 1\nFile message line 2\n"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - - it('should read in a file ending in cr and output a utf8 string', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest2, "format":"utf8", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.have.length(40) - msg.payload.should.equal("File message line 1\nFile message line 2\n"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - it('should read in a file and output split lines with parts', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.should.have.property('topic'); - msg.should.not.have.property('foo'); - msg.should.not.have.property('bar'); - msg.payload.should.be.a.String(); - msg.should.have.property('parts'); - msg.parts.should.have.property('index',c); - msg.parts.should.have.property('type','string'); - msg.parts.should.have.property('ch','\n'); - if (c === 0) { - msg.payload.should.equal("File message line 1"); - } - if (c === 1) { - msg.payload.should.equal("File message line 2"); - } - if (c === 2) { - msg.payload.should.equal(""); - done(); - } - c++; - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"",topic:"A",foo:"bar",bar:"foo"}); - }); - }); - - it('should read in a file with empty line and output split lines with parts', function(done) { - var data = ["-", "", "-", ""]; - var line = data.join("\n"); - fs.writeFileSync(fileToTest, line); - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.equal(data[c]); - msg.should.have.property('parts'); - var parts = msg.parts; - parts.should.have.property('index',c); - parts.should.have.property('type','string'); - parts.should.have.property('ch','\n'); - c++; - if (c === data.length) { - parts.should.have.property('count', data.length); - done(); - } - else { - parts.should.not.have.property('count'); - } - } - catch(e) { - done(e); - } - }); - n1.receive({payload:""}); - }); - }); - - it('should read in a file and output split lines with parts and extra props', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", allProps:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - // console.log(msg) - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.should.have.property('topic'); - msg.should.have.property('foo'); - msg.should.have.property('bar'); - msg.should.have.property('parts'); - msg.parts.should.have.property('index',c); - msg.parts.should.have.property('type','string'); - msg.parts.should.have.property('ch','\n'); - if (c === 0) { - msg.payload.should.equal("File message line 1"); - } - if (c === 1) { - msg.payload.should.equal("File message line 2"); - } - if (c === 2) { - msg.payload.should.equal(""); - done(); - } - c++; - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"",topic:"B",foo:"bar",bar:"foo"}); - }); - }); - - it('should read in a file and output a buffer with parts', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"stream", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.should.have.length(40); - msg.should.have.property('parts'); - msg.parts.should.have.property('count',1); - msg.parts.should.have.property('type','buffer'); - msg.parts.should.have.property('ch',''); - done(); - }); - n1.receive({payload:""}); - }); - }); - - it('should warn if no filename set', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "format":""}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file in"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.equal("file.errors.nofilename"); - done(); - },wait); - n1.receive({}); - }); - }); - - it('should handle a file read error', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":"badfile", "format":"", wires:[["n2"]]}, - {id:"n2", type:"helper"} - ]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - - n2.on("input", function(msg) { - try { - msg.should.not.have.property('payload'); - msg.should.have.property("error"); - msg.error.should.have.property("code","ENOENT"); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file in"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Error"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - describe('encodings', function() { - - function checkReadWithEncoding(enc, data, done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8", encoding:enc, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - - fs.writeFileSync(fileToTest, encode(data, enc)); - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.equal(data); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - } - - // default - it('should read in a file with "none" encoding', function(done) { - checkReadWithEncoding("none", "試験", done); - }); - - // Native - it('should read in a file with "utf8" encoding', function(done) { - checkReadWithEncoding("utf8", "試験", done); - }); - - it('should read in a file with "ucs2" encoding', function(done) { - checkReadWithEncoding("ucs2", "試験", done); - }); - - it('should read in a file with "utf-16le" encoding', function(done) { - checkReadWithEncoding("utf-16le", "試験", done); - }); - - it('should read in a file with "binary" encoding', function(done) { - checkReadWithEncoding("binary", "test", done); - }); - - it('should read in a file with "base64" encoding', function(done) { - checkReadWithEncoding("base64", "5pel5pys6KqeCg==", done); - }); - - it('should read in a file with "hex" encoding', function(done) { - checkReadWithEncoding("hex", "deadbeef", done); - }); - - // Unicode - it('should read in a file with "utf-16be" encoding', function(done) { - checkReadWithEncoding("utf-16be", "試験", done); - }); - - // Japanese - it('should read in a file with "Shift_JIS" encoding', function(done) { - checkReadWithEncoding("Shift_JIS", "試験", done); - }); - - it('should read in a file with "Windows-31j" encoding', function(done) { - checkReadWithEncoding("Windows-31j", "試験", done); - }); - - it('should read in a file with "Windows932" encoding', function(done) { - checkReadWithEncoding("Windows932", "試験", done); - }); - - it('should read in a file with "EUC-JP" encoding', function(done) { - checkReadWithEncoding("EUC-JP", "試験", done); - }); - - // following encoding tests should be more specific - // Chinese - it('should read in a file with "GB2312" encoding', function(done) { - checkReadWithEncoding("GB2312", "test", done); - }); - - it('should read in a file with "GBK" encoding', function(done) { - checkReadWithEncoding("GBK", "test", done); - }); - - it('should read in a file with "GB18030" encoding', function(done) { - checkReadWithEncoding("GB18030", "test", done); - }); - - it('should read in a file with "Windows936" encoding', function(done) { - checkReadWithEncoding("Windows936", "test", done); - }); - - it('should read in a file with "EUC-CN" encoding', function(done) { - checkReadWithEncoding("EUC-CN", "test", done); - }); - - // Korean - it('should read in a file with "KS_C_5601" encoding', function(done) { - checkReadWithEncoding("KS_C_5601", "test", done); - }); - - it('should read in a file with "Windows949" encoding', function(done) { - checkReadWithEncoding("Windows949", "test", done); - }); - - it('should read in a file with "EUC-KR" encoding', function(done) { - checkReadWithEncoding("EUC-KR", "test", done); - }); - - // Taiwan/Hong Kong - it('should read in a file with "Big5" encoding', function(done) { - checkReadWithEncoding("Big5", "test", done); - }); - - it('should read in a file with "Big5-HKSCS" encoding', function(done) { - checkReadWithEncoding("Big5-HKSCS", "test", done); - }); - - it('should read in a file with "Windows950" encoding', function(done) { - checkReadWithEncoding("Windows950", "test", done); - }); - - // Windows - it('should read in a file with "cp874" encoding', function(done) { - checkReadWithEncoding("cp874", "test", done); - }); - - it('should read in a file with "cp1250" encoding', function(done) { - checkReadWithEncoding("cp1250", "test", done); - }); - - it('should read in a file with "cp1251" encoding', function(done) { - checkReadWithEncoding("cp1251", "test", done); - }); - - it('should read in a file with "cp1252" encoding', function(done) { - checkReadWithEncoding("cp1252", "test", done); - }); - - it('should read in a file with "cp1253" encoding', function(done) { - checkReadWithEncoding("cp1253", "test", done); - }); - - it('should read in a file with "cp1254" encoding', function(done) { - checkReadWithEncoding("cp1254", "test", done); - }); - - it('should read in a file with "cp1255" encoding', function(done) { - checkReadWithEncoding("cp1255", "test", done); - }); - - it('should read in a file with "cp1256" encoding', function(done) { - checkReadWithEncoding("cp1256", "test", done); - }); - - it('should read in a file with "cp1257" encoding', function(done) { - checkReadWithEncoding("cp1257", "test", done); - }); - - it('should read in a file with "cp1258" encoding', function(done) { - checkReadWithEncoding("cp1258", "test", done); - }); - - // IBM - it('should read in a file with "cp437" encoding', function(done) { - checkReadWithEncoding("cp437", "test", done); - }); - - it('should read in a file with "cp737" encoding', function(done) { - checkReadWithEncoding("cp737", "test", done); - }); - - it('should read in a file with "cp775" encoding', function(done) { - checkReadWithEncoding("cp775", "test", done); - }); - - it('should read in a file with "cp808" encoding', function(done) { - checkReadWithEncoding("cp808", "test", done); - }); - - it('should read in a file with "cp850" encoding', function(done) { - checkReadWithEncoding("cp850", "test", done); - }); - - it('should read in a file with "cp852" encoding', function(done) { - checkReadWithEncoding("cp852", "test", done); - }); - - it('should read in a file with "cp855" encoding', function(done) { - checkReadWithEncoding("cp855", "test", done); - }); - - it('should read in a file with "cp856" encoding', function(done) { - checkReadWithEncoding("cp856", "test", done); - }); - - it('should read in a file with "cp857" encoding', function(done) { - checkReadWithEncoding("cp857", "test", done); - }); - - it('should read in a file with "cp858" encoding', function(done) { - checkReadWithEncoding("cp858", "test", done); - }); - - it('should read in a file with "cp860" encoding', function(done) { - checkReadWithEncoding("cp860", "test", done); - }); - - it('should read in a file with "cp861" encoding', function(done) { - checkReadWithEncoding("cp861", "test", done); - }); - - it('should read in a file with "cp866" encoding', function(done) { - checkReadWithEncoding("cp866", "test", done); - }); - - it('should read in a file with "cp869" encoding', function(done) { - checkReadWithEncoding("cp869", "test", done); - }); - - it('should read in a file with "cp922" encoding', function(done) { - checkReadWithEncoding("cp922", "test", done); - }); - - it('should read in a file with "cp1046" encoding', function(done) { - checkReadWithEncoding("cp1046", "test", done); - }); - - it('should read in a file with "cp1124" encoding', function(done) { - checkReadWithEncoding("cp1124", "test", done); - }); - - it('should read in a file with "cp1125" encoding', function(done) { - checkReadWithEncoding("cp1125", "test", done); - }); - - it('should read in a file with "cp1129" encoding', function(done) { - checkReadWithEncoding("cp1129", "test", done); - }); - - it('should read in a file with "cp1133" encoding', function(done) { - checkReadWithEncoding("cp1133", "test", done); - }); - - it('should read in a file with "cp1161" encoding', function(done) { - checkReadWithEncoding("cp1161", "test", done); - }); - - it('should read in a file with "cp1162" encoding', function(done) { - checkReadWithEncoding("cp1162", "test", done); - }); - - it('should read in a file with "cp1163" encoding', function(done) { - checkReadWithEncoding("cp1163", "test", done); - }); - - // Mac - it('should read in a file with "maccroatian" encoding', function(done) { - checkReadWithEncoding("maccroatian", "test", done); - }); - - it('should read in a file with "maccyrillic" encoding', function(done) { - checkReadWithEncoding("maccyrillic", "test", done); - }); - - it('should read in a file with "macgreek" encoding', function(done) { - checkReadWithEncoding("macgreek", "test", done); - }); - - it('should read in a file with "maciceland" encoding', function(done) { - checkReadWithEncoding("maciceland", "test", done); - }); - - it('should read in a file with "macroman" encoding', function(done) { - checkReadWithEncoding("macroman", "test", done); - }); - - it('should read in a file with "macromania" encoding', function(done) { - checkReadWithEncoding("macromania", "test", done); - }); - - it('should read in a file with "macthai" encoding', function(done) { - checkReadWithEncoding("macthai", "test", done); - }); - - it('should read in a file with "macturkish" encoding', function(done) { - checkReadWithEncoding("macturkish", "test", done); - }); - - it('should read in a file with "macukraine" encoding', function(done) { - checkReadWithEncoding("macukraine", "test", done); - }); - - it('should read in a file with "maccenteuro" encoding', function(done) { - checkReadWithEncoding("maccenteuro", "test", done); - }); - - it('should read in a file with "macintosh" encoding', function(done) { - checkReadWithEncoding("macintosh", "test", done); - }); - - // KOI8 - it('should read in a file with "koi8-r" encoding', function(done) { - checkReadWithEncoding("koi8-r", "test", done); - }); - - it('should read in a file with "koi8-u" encoding', function(done) { - checkReadWithEncoding("koi8-u", "test", done); - }); - - it('should read in a file with "koi8-ru" encoding', function(done) { - checkReadWithEncoding("koi8-ru", "test", done); - }); - - it('should read in a file with "koi8-t" encoding', function(done) { - checkReadWithEncoding("koi8-t", "test", done); - }); - - // Misc - it('should read in a file with "armscii8" encoding', function(done) { - checkReadWithEncoding("armscii8", "test", done); - }); - - it('should read in a file with "rk1048" encoding', function(done) { - checkReadWithEncoding("rk1048", "test", done); - }); - - it('should read in a file with "tcvn" encoding', function(done) { - checkReadWithEncoding("tcvn", "test", done); - }); - - it('should read in a file with "georgianacademy" encoding', function(done) { - checkReadWithEncoding("georgianacademy", "test", done); - }); - - it('should read in a file with "georgianps" encoding', function(done) { - checkReadWithEncoding("georgianps", "test", done); - }); - - it('should read in a file with "pt154" encoding', function(done) { - checkReadWithEncoding("pt154", "test", done); - }); - - it('should read in a file with "viscii" encoding', function(done) { - checkReadWithEncoding("viscii", "test", done); - }); - - it('should read in a file with "iso646cn" encoding', function(done) { - checkReadWithEncoding("iso646cn", "test", done); - }); - - it('should read in a file with "iso646jp" encoding', function(done) { - checkReadWithEncoding("iso646jp", "test", done); - }); - - it('should read in a file with "hproman8" encoding', function(done) { - checkReadWithEncoding("hproman8", "test", done); - }); - - it('should read in a file with "tis620" encoding', function(done) { - checkReadWithEncoding("tis620", "test", done); - }); - - }); - - }); -}); diff --git a/test/nodes/core/storage/23-watch_spec.js b/test/nodes/core/storage/23-watch_spec.js deleted file mode 100644 index a9942d5b1..000000000 --- a/test/nodes/core/storage/23-watch_spec.js +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var fs = require("fs-extra"); -var path = require("path"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var watchNode = require("nr-test-utils").require("@node-red/nodes/core/storage/23-watch.js"); - - -describe('watch Node', function() { - this.timeout(5000); - - var resourcesDir = path.join(__dirname,"..","..","..","resources"); - var baseDir = path.join(resourcesDir, "23-watch-test-dir"); - var count = 0; - - function prepareDir() { - var dirToWatch = path.join(baseDir, "base"+count); - fs.mkdirSync(dirToWatch); - count++; - return { - dirToWatch:dirToWatch, - file0ToWatch:path.join(dirToWatch, "file0.txt"), - file1ToWatch:path.join(dirToWatch, "file1.txt"), - subDirToWatch:path.join(dirToWatch, "subdir"), - file2ToWatch:path.join(dirToWatch, "subdir", "file2.txt") - } - } - - function wait(msec, func) { - setTimeout(func, msec); - } - - before(function(done) { - fs.ensureDirSync(baseDir); - done(); - }); - - after(function(done) { - fs.removeSync(baseDir); - done(); - }); - - afterEach(function(done) { - helper.unload(); - done(); - }); - - function testWatch(flow, change_func, results, done) { - var processed = {}; - helper.load(watchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - var len = Object.keys(results).length; - n2.on("input", function(msg) { - try { - // console.log(msg); - msg.should.have.property('file'); - - var file = msg.file; - if (file in processed) { - // multiple messages come in rare case - return; - } - processed[file] = true; - if (file === 'subdir') { - // On OSX, we get a change event on subdir when a file inside changes. - // On Travis, we don't. *sigh* - return; - } - (file in results).should.be.true(); - - var result = results[file]; - msg.should.have.property('payload', result.payload); - msg.should.have.property('type', result.type); - if('size' in result) { - msg.should.have.property('size', result.size); - } - count++; - if(count === len) { - n1.close(); - // wait for close - wait(100, done); - } - }catch(err) { - done(err); - } - }); - // wait for preparation - wait(500, change_func); - }); - } - - it('should watch a file to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.file0ToWatch, recursive: false, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file', - 'size': 5 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file0ToWatch, "ABCDE"); - }, results, done); - }); - - it('should watch multiple files to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - fs.writeFileSync(files.file1ToWatch, ''); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.file0ToWatch +","+files.file1ToWatch, recursive: false, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file'//, - // 'size': 5 - }, - 'file1.txt' : { - 'payload' : files.file1ToWatch, - 'topic': files.file1ToWatch, - 'type': 'file'//, - // 'size': 3 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file0ToWatch, "ABCDE"); - fs.appendFileSync(files.file1ToWatch, "123"); - }, results, done); - }); - - it('should watch attribute of a file to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - fs.chmodSync(files.file0ToWatch, 0o444); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.file0ToWatch, recursive: false, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file'//, - // 'size': 0 - } - }; - testWatch(flow, function() { - fs.chmodSync(files.file0ToWatch, 0o777); - }, results, done); - }); - - it('should watch a file in a directory to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.dirToWatch, recursive: true, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file'//, - // 'size': 5 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file0ToWatch, "ABCDE"); - }, results, done); - }); - - it('should watch a sub directory in a directory to be changed', function(done) { - var files = prepareDir(); - fs.mkdirSync(files.subDirToWatch); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.dirToWatch, recursive: true, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file2.txt': { - payload: files.file2ToWatch, - type: 'file'//, - // size: 5 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file2ToWatch, "ABCDE"); - }, results, done); - }); - -}); diff --git a/test/nodes/subflow/subflow_spec.js b/test/nodes/subflow/subflow_spec.js deleted file mode 100644 index d5b8d78ae..000000000 --- a/test/nodes/subflow/subflow_spec.js +++ /dev/null @@ -1,570 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -var should = require("should"); -var functionNode = require("nr-test-utils").require("@node-red/nodes/core/function/10-function.js"); -var helper = require("node-red-node-test-helper"); - -// Notice: -// - nodes should have x, y, z property when defining subflow. - -describe('subflow', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should define subflow', function(done) { - var flow = [ - {id:"t1", type:"tab"}, - {id:"n1", z:"t1", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", z:"t1", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{wires:[ {id:"s1-n1"} ]}], - out:[{wires:[ {id:"s1-n1", port:0} ]}]}, - {id:"s1-n1", z:"s1", type:"function", - func:"return msg;", wires:[]} - ]; - helper.load(functionNode, flow, function() { - done(); - }); - }); - - it('should pass data to/from subflow', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.payload = msg.payload+'bar'; return msg;", wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "foobar"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should pass data to/from nested subflow', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow1 - {id:"s1", type:"subflow", name:"Subflow1", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n2", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"subflow:s2", - wires:[["s1-n2"]]}, - {id:"s1-n2", x:10, y:10, z:"s1", type:"function", - func:"msg.payload = msg.payload+'baz'; return msg;", wires:[]}, - // Subflow2 - {id:"s2", type:"subflow", name:"Subflow2", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s2-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s2-n1", port:0} ] - }] - }, - {id:"s2-n1", x:10, y:10, z:"s2", type:"function", - func:"msg.payload=msg.payload+'bar'; return msg;", wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "foobarbaz"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of subflow template', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - env: [ - {name: "K", type: "str", value: "V"} - ], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of subflow instance', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access last env var with same name', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V0"}, - {name: "X", type: "str", value: "VX"}, - {name: "K", type: "str", value: "V1"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V1"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access typed value of env var', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "KN", type: "num", value: "100"}, - {name: "KB", type: "bool", value: "true"}, - {name: "KJ", type: "json", value: "[1,2,3]"}, - {name: "Kb", type: "bin", value: "[65,65]"}, - {name: "Ke", type: "env", value: "KS"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }], - env: [ - {name: "KS", type: "str", value: "STR"} - ] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.VE = env.get('Ke'); msg.VS = env.get('KS'); msg.VN = env.get('KN'); msg.VB = env.get('KB'); msg.VJ = env.get('KJ'); msg.Vb = env.get('Kb'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("VS", "STR"); - msg.should.have.property("VN", 100); - msg.should.have.property("VB", true); - msg.should.have.property("VJ", [1,2,3]); - msg.should.have.property("Vb"); - should.ok(msg.Vb instanceof Buffer); - msg.should.have.property("VE","STR"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should overwrite env var of subflow template by env var of subflow instance', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - env: [ - {name: "K", type: "str", value: "TV"} - ], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of parent subflow template', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow1 - {id:"s1", type:"subflow", name:"Subflow1", info:"", - env: [ - {name: "K", type: "str", value: "V"}, - ], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n2", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"subflow:s2", - wires:[["s1-n2"]]}, - {id:"s1-n2", x:10, y:10, z:"s1", type:"function", - func:"return msg;", wires:[]}, - // Subflow2 - {id:"s2", type:"subflow", name:"Subflow2", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s2-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s2-n1", port:0} ] - }] - }, - {id:"s2-n1", x:10, y:10, z:"s2", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("V", "V"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of parent subflow instance', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow1 - {id:"s1", type:"subflow", name:"Subflow1", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n2", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"subflow:s2", - wires:[["s1-n2"]]}, - {id:"s1-n2", x:10, y:10, z:"s1", type:"function", - func:"return msg;", wires:[]}, - // Subflow2 - {id:"s2", type:"subflow", name:"Subflow2", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s2-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s2-n1", port:0} ] - }] - }, - {id:"s2-n1", x:10, y:10, z:"s2", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("V", "V"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of tab', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:"", env: [ - {name: "K", type: "str", value: "V"} - ]}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", env: [], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of group', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"g1", z:"t0", type:"group", env:[ - {name: "K", type: "str", value: "V"} - ]}, - {id:"n1", x:10, y:10, z:"t0", g:"g1", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", env: [], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of nested group', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"g1", z:"t0", type:"group", env:[ - {name: "K", type: "str", value: "V"} - ]}, - {id:"g2", z:"t0", g:"g1", type:"group", env:[]}, - {id:"n1", x:10, y:10, z:"t0", g:"g2", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", env: [], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - -}); diff --git a/test/resources/70-HTML-test-file.html b/test/resources/70-HTML-test-file.html deleted file mode 100644 index 2187b8e31..000000000 --- a/test/resources/70-HTML-test-file.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - -

    This is a test page for node 70-HTML

    - -

    There's nothing to read here.

    - -
      -
    1. Blue
    2. -
    3. Red
    4. -
    - -
      -
    • Apple
    • -
    • Pear
    • -
    - -
      -
    • Potato
    • -
    • Parsnip
    • -
    - - - - - - - diff --git a/test/resources/file-in-node/test.txt b/test/resources/file-in-node/test.txt deleted file mode 100644 index 68ca53b4d..000000000 --- a/test/resources/file-in-node/test.txt +++ /dev/null @@ -1 +0,0 @@ -Text file \ No newline at end of file diff --git a/test/resources/icons/test_icon.png b/test/resources/icons/test_icon.png deleted file mode 100644 index 4b6b7b5e9b10ec790348eb59f73bdac596e2f3b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1$P6UK?yS)OQjEnx?oJHr&dIz4a@YcVLR^9L z|NsA&-kg6IB%S2#?!wT)D(eB{a29w(76Vni0bxeDQVUa{AbW|YuPggKZdPtVo=;b% zu>pm|JY5_^IIbrr*#Br`l>NUU`M`mYFm{Gnm$g4<{K@tLs$lSR^>bP0l+XkK5O^!s diff --git a/test/resources/plugin/test-plugin/library-filestore.html b/test/resources/plugin/test-plugin/library-filestore.html deleted file mode 100644 index 60fbae464..000000000 --- a/test/resources/plugin/test-plugin/library-filestore.html +++ /dev/null @@ -1,22 +0,0 @@ - \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/library-filestore.js b/test/resources/plugin/test-plugin/library-filestore.js deleted file mode 100644 index 7c6f1db83..000000000 --- a/test/resources/plugin/test-plugin/library-filestore.js +++ /dev/null @@ -1,37 +0,0 @@ - -module.exports = function(RED) { - const PLUGIN_TYPE_ID = "node-red-library-filestore"; - - class FileStorePlugin { - constructor(config) { - this.type = PLUGIN_TYPE_ID; - this.id = config.id; - this.label = config.label; - this.config = config.config; - this.icon = config.icon; - - console.log("FileStorePlugin",config) - } - async init() { - console.log("FileStorePlugin.init") - - } - async getEntry(type,path) { - console.log("FileStorePlugin.getLibraryEntry",type,path) - return [] - } - async saveEntry(type,path,meta,body) { - console.log("FileStorePlugin.saveLibraryEntry",type,path) - } - } - - - RED.plugins.registerPlugin(PLUGIN_TYPE_ID, { - type: "node-red-library-source", - class: FileStorePlugin, - defaults: { - "path": { value: "" }, - // "secret": { type: "password" } - } - }) -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/locales/en-US/library-filestore.json b/test/resources/plugin/test-plugin/locales/en-US/library-filestore.json deleted file mode 100644 index b9ffa5f17..000000000 --- a/test/resources/plugin/test-plugin/locales/en-US/library-filestore.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": { - "path": "Path" - }, - "desc": { - "path":"The local file-system path to the library" - } -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json b/test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json deleted file mode 100644 index e990e2ec1..000000000 --- a/test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugin": "winning" -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/package.json b/test/resources/plugin/test-plugin/package.json deleted file mode 100644 index 65487c656..000000000 --- a/test/resources/plugin/test-plugin/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "test-plugin", - "version": "1.0.0", - "description": "", - "node-red": { - "plugins": { - "test": "test.js", - "test-editor-plugin": "test-editor-plugin.html", - "test-runtime-plugin": "test-runtime-plugin.js", - "library-filestore": "library-filestore.js" - } - } -} diff --git a/test/resources/plugin/test-plugin/test-editor-plugin.html b/test/resources/plugin/test-plugin/test-editor-plugin.html deleted file mode 100644 index 177813526..000000000 --- a/test/resources/plugin/test-plugin/test-editor-plugin.html +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/test-runtime-plugin.js b/test/resources/plugin/test-plugin/test-runtime-plugin.js deleted file mode 100644 index d9e3ff3c7..000000000 --- a/test/resources/plugin/test-plugin/test-runtime-plugin.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = function(RED) { - console.log("Loaded test-plugin/test-runtime-plugin") - - RED.plugins.registerPlugin("my-test-runtime-only-plugin", { - type: "bar", - onadd: function() { - console.log("my-test-runtime-only-plugin.onadd called") - } - }) -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/test.html b/test/resources/plugin/test-plugin/test.html deleted file mode 100644 index 7a43a0528..000000000 --- a/test/resources/plugin/test-plugin/test.html +++ /dev/null @@ -1,6 +0,0 @@ - - -"); - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns node module info', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleInfo: function(opts) { - return Promise.resolve({"node-red":{name:"node-red"}}[opts.module]); - } - } - }); - request(app) - .get('/nodes/node-red') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - done(); - }); - }); - - it('returns 404 for unknown module', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleInfo: function(opts) { - var errInstance = new Error("Not Found"); - errInstance.code = "not_found"; - errInstance.status = 404; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .get('/nodes/node-blue') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns individual node info', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getNodeInfo: function(opts) { - return Promise.resolve({"node-red/123":{id:"node-red/123"}}[opts.id]); - } - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","node-red/123"); - done(); - }); - }); - - it('returns individual node configs', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getNodeConfig: function(opts) { - return Promise.resolve({"node-red/123":""}[opts.id]); - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - it('returns 404 for unknown node', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getNodeInfo: function(opts) { - var errInstance = new Error("Not Found"); - errInstance.code = "not_found"; - errInstance.status = 404; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .get('/nodes/node-red/456') - .set('Accept', 'application/json') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - }); - - describe('install', function() { - it('installs the module and returns module info', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - addModule: function(_opts) { - opts = _opts; - return Promise.resolve({ - name:"foo", - nodes:[{id:"123"}] - }); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","foo"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("id","123"); - opts.should.have.property("module","foo"); - opts.should.have.property("version","1.2.3"); - opts.should.have.property("url","https://example/foo-1.2.3.tgz"); - done(); - }); - }); - it('returns error', function(done) { - nodes.init({ - settings: {}, - nodes:{ - addModule: function(opts) { - var errInstance = new Error("Message"); - errInstance.code = "random_error"; - errInstance.status = 400; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.a.property('code','random_error'); - done(); - }); - }); - }); - describe('delete', function() { - it('uninstalls the module', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - removeModule: function(_opts) { - opts = _opts; - return Promise.resolve(); - } - } - }); - request(app) - .del('/nodes/123') - .expect(204) - .end(function(err,res) { - if (err) { - throw err; - } - opts.should.have.property("module","123"); - done(); - }); - }); - it('returns error', function(done) { - nodes.init({ - settings: {}, - nodes:{ - removeModule: function(opts) { - var errInstance = new Error("Message"); - errInstance.code = "random_error"; - errInstance.status = 400; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .del('/nodes/123') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.a.property('code','random_error'); - done(); - }); - }); - }); - - describe('enable/disable node set', function() { - it('returns 400 for invalid request payload', function(done) { - nodes.init({ - settings: {}, - nodes:{ - setNodeSetState: function(opts) {return Promise.resolve()} - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("code","invalid_request"); - res.body.should.have.property("message","Invalid request"); - done(); - }); - }); - - it('sets node state and returns node info', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - setNodeSetState: function(_opts) { - opts = _opts; - return Promise.resolve({id:"123",enabled: true }); - } - } - }); - - request(app) - .put('/nodes/node-red/foo') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",true); - opts.should.have.property("enabled",true); - opts.should.have.property("id","node-red/foo"); - - done(); - }); - }); - }); - describe('enable/disable module' ,function() { - it('returns 400 for invalid request payload', function(done) { - nodes.init({ - settings: {}, - nodes:{ - setModuleState: function(opts) {return Promise.resolve()} - } - }); - request(app) - .put('/nodes/node-red') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("code","invalid_request"); - res.body.should.have.property("message","Invalid request"); - done(); - }); - }); - it('sets module state and returns module info', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - setModuleState: function(_opts) { - opts = _opts; - return Promise.resolve({name:"node-red"}); - } - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - opts.should.have.property("enabled",true); - opts.should.have.property("module","node-red"); - - done(); - }); - }); - }); - - describe('get icons', function() { - it('returns icon list', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getIconList: function() { - return Promise.resolve({module:[1,2,3]}); - } - } - }); - request(app) - .get('/getIcons') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("module"); - res.body.module.should.be.an.Array(); - res.body.module.should.have.lengthOf(3); - done(); - }); - }); - }); - - describe('get module messages', function() { - it('returns message catalog', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleCatalog: function(opts) { - return Promise.resolve({a:123}); - } - } - }); - request(app) - .get('/nodes/module/set/messages') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.eql({a:123}); - done(); - }); - }); - it('returns all node catalogs', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleCatalogs: function(opts) { - return Promise.resolve({a:1}); - } - } - }); - request(app) - .get('/nodes/messages') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.eql({a:1}); - done(); - }); - }); - }) -}); diff --git a/test/unit/@node-red/editor-api/lib/admin/plugins_spec.js b/test/unit/@node-red/editor-api/lib/admin/plugins_spec.js deleted file mode 100644 index 74584a1d8..000000000 --- a/test/unit/@node-red/editor-api/lib/admin/plugins_spec.js +++ /dev/null @@ -1,111 +0,0 @@ -const should = require("should"); -const request = require('supertest'); -const express = require('express'); -const bodyParser = require("body-parser"); - -var app; - -var NR_TEST_UTILS = require("nr-test-utils"); - -var plugins = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/plugins"); - -describe("api/editor/plugins", function() { - const pluginList = [ - { - "id": "test-module/test-set", - "enabled": true, - "local": false, - "plugins": [ - { - "type": "foo", - "id": "a-plugin", - "module": "test-module" - }, - { - "type": "bar", - "id": "a-plugin2", - "module": "test-module" - }, - { - "type": "foo", - "id": "a-plugin3", - "module": "test-module" - } - ] - }, - { - "id": "test-module/test-disabled-set", - "enabled": false, - "local": false, - "plugins": [] - } - ]; - const pluginConfigs = ` - -test-module-config`; - - const pluginCatalogs = { "test-module": {"foo": "bar"}}; - - before(function() { - app = express(); - app.use(bodyParser.json()); - app.get("/plugins",plugins.getAll); - app.get("/plugins/messages",plugins.getCatalogs); - - plugins.init({ - plugins: { - getPluginList: async function() { return pluginList }, - getPluginConfigs: async function() { return pluginConfigs }, - getPluginCatalogs: async function() { return pluginCatalogs } - } - }) - }); - - it('returns the list of plugins', function(done) { - request(app) - .get("/plugins") - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - try { - JSON.stringify(res.body).should.eql(JSON.stringify(pluginList)); - done(); - } catch(err) { - done(err) - } - }); - }); - it('returns the plugin configs', function(done) { - request(app) - .get("/plugins") - .set('Accept', 'text/html') - .expect(200) - .expect(pluginConfigs) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - it('returns the plugin catalogs', function(done) { - request(app) - .get("/plugins/messages") - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - try { - JSON.stringify(res.body).should.eql(JSON.stringify(pluginCatalogs)); - done(); - } catch(err) { - done(err) - } - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/admin/settings_spec.js b/test/unit/@node-red/editor-api/lib/admin/settings_spec.js deleted file mode 100644 index 6180f903d..000000000 --- a/test/unit/@node-red/editor-api/lib/admin/settings_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require("body-parser"); -var sinon = require('sinon'); - -var app; - -var NR_TEST_UTILS = require("nr-test-utils"); - -var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/settings"); -var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); - -describe("api/editor/settings", function() { - before(function() { - sinon.stub(theme,"settings").callsFake(function() { return { existing: 123, test: 456 };}); - app = express(); - app.use(bodyParser.json()); - app.get("/settings",info.runtimeSettings); - }); - - after(function() { - theme.settings.restore(); - }); - - it('returns the runtime settings', function(done) { - info.init({},{ - settings: { - getRuntimeSettings: function(opts) { - return Promise.resolve({ - a:1, - b:2, - editorTheme: { existing: 789 } - }) - } - } - }); - request(app) - .get("/settings") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("a",1); - res.body.should.have.property("b",2); - res.body.should.have.property("editorTheme",{existing: 789, test:456}); - done(); - }); - }); - it('returns the runtime settings - disableEditor true', function(done) { - info.init({disableEditor: true},{ - settings: { - getRuntimeSettings: function(opts) { - return Promise.resolve({ - a:1, - b:2 - }) - } - } - }); - request(app) - .get("/settings") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("a",1); - res.body.should.have.property("b",2); - // no editorTheme if disabledEditor true - res.body.should.not.have.property("editorTheme"); - done(); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/clients_spec.js b/test/unit/@node-red/editor-api/lib/auth/clients_spec.js deleted file mode 100644 index 1f6263d00..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/clients_spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var NR_TEST_UTILS = require("nr-test-utils"); -var Clients = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/clients"); - -describe("api/auth/clients", function() { - it('finds the known editor client',function(done) { - Clients.get("node-red-editor").then(function(client) { - client.should.have.property("id","node-red-editor"); - client.should.have.property("secret","not_available"); - done(); - }); - }); - it('finds the known admin client',function(done) { - Clients.get("node-red-admin").then(function(client) { - client.should.have.property("id","node-red-admin"); - client.should.have.property("secret","not_available"); - done(); - }).catch(function(err) { - done(err); - }); - }); - it('returns null for unknown client',function(done) { - Clients.get("unknown-client").then(function(client) { - should.not.exist(client); - done(); - }).catch(function(err) { - done(err); - }); - - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/index_spec.js b/test/unit/@node-red/editor-api/lib/auth/index_spec.js deleted file mode 100644 index 010b6b08a..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/index_spec.js +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var passport = require("passport"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth"); -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); -var Permissions = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/permissions"); - -describe("api/auth/index",function() { - - - - describe("ensureClientSecret", function() { - before(function() { - auth.init({},{}) - }); - it("leaves client_secret alone if not present",function(done) { - var req = { - body: { - client_secret: "test_value" - } - }; - auth.ensureClientSecret(req,null,function() { - req.body.should.have.a.property("client_secret","test_value"); - done(); - }) - }); - it("applies a default client_secret if not present",function(done) { - var req = { - body: { } - }; - auth.ensureClientSecret(req,null,function() { - req.body.should.have.a.property("client_secret","not_available"); - done(); - }) - }); - }); - - describe("revoke", function() { - it("revokes a token", function(done) { - var revokeToken = sinon.stub(Tokens,"revoke").callsFake(function() { - return Promise.resolve(); - }); - - var req = { body: { token: "abcdef" } }; - - var res = { status: function(resp) { - revokeToken.restore(); - - resp.should.equal(200); - return { - end: done - } - }}; - - auth.revoke(req,res); - }); - }); - - describe("login", function() { - beforeEach(function() { - sinon.stub(Tokens,"init").callsFake(function(){}); - sinon.stub(Users,"init").callsFake(function(){}); - }); - afterEach(function() { - Tokens.init.restore(); - Users.init.restore(); - }); - it("returns login details - credentials", function(done) { - auth.init({adminAuth:{type:"credentials"}},{}) - auth.login(null,{json: function(resp) { - resp.should.have.a.property("type","credentials"); - resp.should.have.a.property("prompts"); - resp.prompts.should.have.a.lengthOf(2); - done(); - }}); - }); - it("returns login details - none", function(done) { - auth.init({},{}) - auth.login(null,{json: function(resp) { - resp.should.eql({}); - done(); - }}); - }); - it("returns login details - strategy", function(done) { - auth.init({adminAuth:{type:"strategy",strategy:{label:"test-strategy",icon:"test-icon"}}},{}) - auth.login(null,{json: function(resp) { - resp.should.have.a.property("type","strategy"); - resp.should.have.a.property("prompts"); - resp.prompts.should.have.a.lengthOf(1); - resp.prompts[0].should.have.a.property("type","button"); - resp.prompts[0].should.have.a.property("label","test-strategy"); - resp.prompts[0].should.have.a.property("icon","test-icon"); - - done(); - }}); - }); - - }); - describe("needsPermission", function() { - beforeEach(function() { - sinon.stub(Tokens,"init").callsFake(function(){}); - sinon.stub(Users,"init").callsFake(function(){}); - }); - afterEach(function() { - Tokens.init.restore(); - Users.init.restore(); - if (passport.authenticate.restore) { - passport.authenticate.restore(); - } - if (Permissions.hasPermission.restore) { - Permissions.hasPermission.restore(); - } - }); - - - it('no-ops if adminAuth not set', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - } - }); - auth.init({}); - var func = auth.needsPermission("foo"); - func({},{},function() { - passport.authenticate.called.should.be.false(); - done(); - }) - }); - it('skips auth if req.user undefined', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - next(); - } - }); - sinon.stub(Permissions,"hasPermission").callsFake(function(perm) { return true }); - auth.init({adminAuth:{}}); - var func = auth.needsPermission("foo"); - func({user:null},{},function() { - try { - passport.authenticate.called.should.be.true(); - Permissions.hasPermission.called.should.be.false(); - done(); - } catch(err) { - done(err); - } - }) - }); - - it('passes for valid user permission', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - next(); - } - }); - sinon.stub(Permissions,"hasPermission").callsFake(function(perm) { return true }); - auth.init({adminAuth:{}}); - var func = auth.needsPermission("foo"); - func({user:true,authInfo: { scope: "read"}},{},function() { - try { - passport.authenticate.called.should.be.true(); - Permissions.hasPermission.called.should.be.true(); - Permissions.hasPermission.lastCall.args[0].should.eql("read"); - Permissions.hasPermission.lastCall.args[1].should.eql("foo"); - done(); - } catch(err) { - done(err); - } - }) - }); - - it('rejects for invalid user permission', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - next(); - } - }); - sinon.stub(Permissions,"hasPermission").callsFake(function(perm) { return false }); - auth.init({adminAuth:{}}); - var func = auth.needsPermission("foo"); - func({user:true,authInfo: { scope: "read"}},{ - status: function(status) { - return { end: function() { - try { - status.should.eql(401); - done(); - } catch(err) { - done(err); - } - }} - } - },function() { - done(new Error("hasPermission unexpected passed")) - }); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/permissions_spec.js b/test/unit/@node-red/editor-api/lib/auth/permissions_spec.js deleted file mode 100644 index 50c249c34..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/permissions_spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var permissions = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/permissions"); - -describe("api/auth/permissions", function() { - describe("hasPermission", function() { - it('a user with no permissions',function() { - permissions.hasPermission([],"*").should.be.false(); - }); - it('a user with global permissions',function() { - permissions.hasPermission("*","read").should.be.true(); - permissions.hasPermission(["*"],"write").should.be.true(); - }); - it('a user with read permissions',function() { - permissions.hasPermission(["read"],"read").should.be.true(); - permissions.hasPermission(["read"],"node.read").should.be.true(); - permissions.hasPermission(["read"],"write").should.be.false(); - permissions.hasPermission(["read"],"node.write").should.be.false(); - permissions.hasPermission(["*.read"],"read").should.be.true(); - permissions.hasPermission(["*.read"],"node.read").should.be.true(); - permissions.hasPermission(["*.read"],"write").should.be.false(); - permissions.hasPermission(["*.read"],"node.write").should.be.false(); - }); - it('a user with foo permissions',function() { - permissions.hasPermission("foo","foo").should.be.true(); - }); - it('an array of permissions', function() { - permissions.hasPermission(["*"],["foo.read","foo.write"]).should.be.true(); - permissions.hasPermission("read",["foo.read","foo.write"]).should.be.false(); - permissions.hasPermission("read",["foo.read","bar.read"]).should.be.true(); - permissions.hasPermission(["flows.read"],["flows.read"]).should.be.true(); - permissions.hasPermission(["flows.read"],["flows.write"]).should.be.false(); - permissions.hasPermission(["flows.read","nodes.write"],["flows.write"]).should.be.false(); - permissions.hasPermission(["flows.read","nodes.write"],["nodes.write"]).should.be.true(); - }); - it('permits an empty permission', function() { - permissions.hasPermission("*","").should.be.true(); - permissions.hasPermission("read",[""]).should.be.true(); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/strategies_spec.js b/test/unit/@node-red/editor-api/lib/auth/strategies_spec.js deleted file mode 100644 index 9fafc11ed..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/strategies_spec.js +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var strategies = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/strategies"); -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); -var Clients = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/clients"); - -describe("api/auth/strategies", function() { - describe("Password Token Exchange", function() { - var userAuthentication; - afterEach(function() { - if (userAuthentication) { - userAuthentication.restore(); - userAuthentication = null; - } - }); - - it('Handles authentication failure',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve(null); - }); - - strategies.passwordTokenExchange({},"user","password","scope",function(err,token) { - try { - should.not.exist(err); - token.should.be.false(); - done(); - } catch(e) { - done(e); - } - }); - }); - - it('Handles scope overreach',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve({username:"user",permissions:"read"}); - }); - - strategies.passwordTokenExchange({},"user","password","*",function(err,token) { - try { - should.not.exist(err); - token.should.be.false(); - done(); - } catch(e) { - done(e); - } - }); - }); - - it('Creates new token on authentication success',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve({username:"user",permissions:"*"}); - }); - var tokenDetails = {}; - var tokenCreate = sinon.stub(Tokens,"create").callsFake(function(username,client,scope) { - tokenDetails.username = username; - tokenDetails.client = client; - tokenDetails.scope = scope; - return Promise.resolve({accessToken: "123456"}); - }); - - strategies.passwordTokenExchange({id:"myclient"},"user","password","read",function(err,token) { - try { - should.not.exist(err); - token.should.equal("123456"); - tokenDetails.should.have.property("username","user"); - tokenDetails.should.have.property("client","myclient"); - tokenDetails.should.have.property("scope","read"); - done(); - } catch(e) { - done(e); - } finally { - tokenCreate.restore(); - } - }); - - }); - }); - - describe("Anonymous Strategy", function() { - it('Succeeds if anon user enabled',function(done) { - var userDefault = sinon.stub(Users,"default").callsFake(function() { - return Promise.resolve("anon"); - }); - strategies.anonymousStrategy._success = strategies.anonymousStrategy.success; - strategies.anonymousStrategy.success = function(user) { - user.should.equal("anon"); - strategies.anonymousStrategy.success = strategies.anonymousStrategy._success; - delete strategies.anonymousStrategy._success; - done(); - }; - strategies.anonymousStrategy.authenticate({}); - }); - it('Fails if anon user not enabled',function(done) { - var userDefault = sinon.stub(Users,"default").callsFake(function() { - return Promise.resolve(null); - }); - strategies.anonymousStrategy._fail = strategies.anonymousStrategy.fail; - strategies.anonymousStrategy.fail = function(err) { - err.should.equal(401); - strategies.anonymousStrategy.fail = strategies.anonymousStrategy._fail; - delete strategies.anonymousStrategy._fail; - done(); - }; - strategies.anonymousStrategy.authenticate({}); - }); - afterEach(function() { - Users.default.restore(); - }) - }); - - describe("Tokens Strategy", function() { - it('Succeeds if tokens user enabled custom header',function(done) { - var userTokens = sinon.stub(Users,"tokens").callsFake(function(token) { - return Promise.resolve("tokens-"+token); - }); - var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) { - return "x-test-token"; - }); - strategies.tokensStrategy._success = strategies.tokensStrategy.success; - strategies.tokensStrategy.success = function(user) { - user.should.equal("tokens-1234"); - strategies.tokensStrategy.success = strategies.tokensStrategy._success; - delete strategies.tokensStrategy._success; - done(); - }; - strategies.tokensStrategy.authenticate({headers:{"x-test-token":"1234"}}); - }); - it('Succeeds if tokens user enabled default header',function(done) { - var userTokens = sinon.stub(Users,"tokens").callsFake(function(token) { - return Promise.resolve("tokens-"+token); - }); - var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) { - return "authorization"; - }); - strategies.tokensStrategy._success = strategies.tokensStrategy.success; - strategies.tokensStrategy.success = function(user) { - user.should.equal("tokens-1234"); - strategies.tokensStrategy.success = strategies.tokensStrategy._success; - delete strategies.tokensStrategy._success; - done(); - }; - strategies.tokensStrategy.authenticate({headers:{"authorization":"Bearer 1234"}}); - }); - it('Fails if tokens user not enabled',function(done) { - var userTokens = sinon.stub(Users,"tokens").callsFake(function() { - return Promise.resolve(null); - }); - var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) { - return "authorization"; - }); - strategies.tokensStrategy._fail = strategies.tokensStrategy.fail; - strategies.tokensStrategy.fail = function(err) { - err.should.equal(401); - strategies.tokensStrategy.fail = strategies.tokensStrategy._fail; - delete strategies.tokensStrategy._fail; - done(); - }; - strategies.tokensStrategy.authenticate({headers:{"authorization":"Bearer 1234"}}); - }); - afterEach(function() { - Users.tokens.restore(); - Users.tokenHeader.restore(); - }) - }); - - describe("Bearer Strategy", function() { - it('Rejects invalid token',function(done) { - var getToken = sinon.stub(Tokens,"get").callsFake(function(token) { - return Promise.resolve(null); - }); - - strategies.bearerStrategy("1234",function(err,user) { - try { - should.not.exist(err); - user.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - getToken.restore(); - } - }); - }); - it('Accepts valid token',function(done) { - var getToken = sinon.stub(Tokens,"get").callsFake(function(token) { - return Promise.resolve({user:"user",scope:"scope"}); - }); - var getUser = sinon.stub(Users,"get").callsFake(function(username) { - return Promise.resolve("aUser"); - }); - - strategies.bearerStrategy("1234",function(err,user,opts) { - try { - should.not.exist(err); - user.should.equal("aUser"); - opts.should.have.a.property("scope","scope"); - done(); - } catch(e) { - done(e); - } finally { - getToken.restore(); - getUser.restore(); - } - }); - }); - it('Fail if no user for token',function(done) { - var getToken = sinon.stub(Tokens,"get").callsFake(function(token) { - return Promise.resolve({user:"user",scope:"scope"}); - }); - var getUser = sinon.stub(Users,"get").callsFake(function(username) { - return Promise.resolve(null); - }); - - strategies.bearerStrategy("1234",function(err,user,opts) { - try { - should.not.exist(err); - user.should.equal(false); - should.not.exist(opts); - done(); - } catch(e) { - done(e); - } finally { - getToken.restore(); - getUser.restore(); - } - }); - }); - }); - - describe("Client Password Strategy", function() { - it('Accepts valid client',function(done) { - var testClient = {id:"node-red-editor",secret:"not_available"}; - var getClient = sinon.stub(Clients,"get").callsFake(function(client) { - return Promise.resolve(testClient); - }); - - strategies.clientPasswordStrategy(testClient.id,testClient.secret,function(err,client) { - try { - should.not.exist(err); - client.should.eql(testClient); - done(); - } catch(e) { - done(e); - } finally { - getClient.restore(); - } - }); - }); - it('Rejects invalid client secret',function(done) { - var testClient = {id:"node-red-editor",secret:"not_available"}; - var getClient = sinon.stub(Clients,"get").callsFake(function(client) { - return Promise.resolve(testClient); - }); - - strategies.clientPasswordStrategy(testClient.id,"invalid_secret",function(err,client) { - try { - should.not.exist(err); - client.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - getClient.restore(); - } - }); - }); - it('Rejects invalid client id',function(done) { - var getClient = sinon.stub(Clients,"get").callsFake(function(client) { - return Promise.resolve(null); - }); - strategies.clientPasswordStrategy("invalid_id","invalid_secret",function(err,client) { - try { - should.not.exist(err); - client.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - getClient.restore(); - } - }); - }); - - var userAuthentication; - it('Blocks after 5 failures',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve(null); - }); - for (var z=0; z<5; z++) { - strategies.passwordTokenExchange({},"user","badpassword","scope",function(err,token) { - }); - } - strategies.passwordTokenExchange({},"user","badpassword","scope",function(err,token) { - try { - err.toString().should.equal("Error: Too many login attempts. Wait 10 minutes and try again"); - token.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - userAuthentication.restore(); - } - }); - }); - - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/tokens_spec.js b/test/unit/@node-red/editor-api/lib/auth/tokens_spec.js deleted file mode 100644 index 9bb231eb5..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/tokens_spec.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); - - -describe("api/auth/tokens", function() { - describe("#init",function() { - it('loads sessions', function(done) { - Tokens.init({}).then(done); - }); - }); - - - describe("#get",function() { - it('returns a valid token', function(done) { - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({"1234":{"user":"fred","expires":Date.now()+1000}}); - } - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - token.should.have.a.property("user","fred"); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('returns null for an invalid token', function(done) { - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({}); - } - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - should.not.exist(token); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - it('returns null for an expired token', function(done) { - var saveSessions = sinon.stub().returns(Promise.resolve()); - var expiryTime = Date.now()+50; - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({"1234":{"user":"fred","expires":expiryTime}}); - }, - saveSessions: saveSessions - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - should.exist(token); - setTimeout(function() { - Tokens.get("1234").then(function(token) { - try { - should.not.exist(token); - saveSessions.calledOnce.should.be.true(); - done(); - } catch(err) { - done(err); - } - }); - },100); - } catch(err) { - done(err); - } - }); - }); - }); - - it('returns a valid api token', function(done) { - Tokens.init({ - tokens: [{ - token: "1234", - user: "fred", - }] - },{ - getSessions:function() { - return Promise.resolve({}); - } - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - token.should.have.a.property("user","fred"); - done(); - } catch(err) { - done(err); - } - }); - }); - - }); - }); - - describe("#create",function() { - it('creates a token', function(done) { - var savedSession; - Tokens.init({sessionExpiryTime: 10},{ - getSessions:function() { - return Promise.resolve({}); - }, - saveSessions:function(sess) { - savedSession = sess; - return Promise.resolve(); - } - }); - var expectedExpiryTime = Date.now()+10000; - - - Tokens.create("user","client","scope").then(function(token) { - try { - should.exist(savedSession); - var sessionKeys = Object.keys(savedSession); - sessionKeys.should.have.lengthOf(1); - - token.should.have.a.property('accessToken',sessionKeys[0]); - savedSession[sessionKeys[0]].should.have.a.property('user','user'); - savedSession[sessionKeys[0]].should.have.a.property('client','client'); - savedSession[sessionKeys[0]].should.have.a.property('scope','scope'); - savedSession[sessionKeys[0]].should.have.a.property('expires'); - savedSession[sessionKeys[0]].expires.should.be.within(expectedExpiryTime-200,expectedExpiryTime+200); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - describe("#revoke", function() { - it('revokes a token', function(done) { - var savedSession; - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({"1234":{"user":"fred","expires":Date.now()+1000}}); - }, - saveSessions:function(sess) { - savedSession = sess; - return Promise.resolve(); - } - }).then(function() { - Tokens.revoke("1234").then(function() { - try { - savedSession.should.not.have.a.property("1234"); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/users_spec.js b/test/unit/@node-red/editor-api/lib/auth/users_spec.js deleted file mode 100644 index e07a35a03..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/users_spec.js +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); - -describe("api/auth/users", function() { - after(function() { - Users.init({}); - }) - describe('Initalised with a credentials object, no anon',function() { - before(function() { - Users.init({ - type:"credentials", - users:{ - username:"fred", - password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK', - // 'password' -> require('bcryptjs').hashSync('password', 8); - permissions:"*" - } - }); - }); - describe('#get',function() { - it('returns known user',function(done) { - Users.get("fred").then(function(user) { - try { - user.should.have.a.property("username","fred"); - user.should.have.a.property("permissions","*"); - user.should.not.have.a.property("password"); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('returns null for unknown user', function(done) { - Users.get("barney").then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - describe('#default',function() { - it('returns null for default user', function(done) { - Users.default().then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - describe('#authenticate',function() { - it('authenticates a known user', function(done) { - Users.authenticate('fred','password').then(function(user) { - try { - user.should.have.a.property("username","fred"); - user.should.have.a.property("permissions","*"); - user.should.not.have.a.property("password"); - done(); - } catch(err) { - done(err); - } - }); - }); - it('rejects invalid password for a known user', function(done) { - Users.authenticate('fred','wrong').then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('rejects invalid user', function(done) { - Users.authenticate('barney','wrong').then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initalised with a credentials object including anon',function() { - before(function() { - Users.init({ - type:"credentials", - users:[], - default: { permissions: "*" } - }); - }); - describe('#default',function() { - it('returns default user', function(done) { - Users.default().then(function(user) { - try { - user.should.have.a.property('anonymous',true); - user.should.have.a.property('permissions','*'); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initialised with a credentials object with user functions',function() { - var authUsername = ''; - var authPassword = ''; - before(function() { - Users.init({ - type:"credentials", - users:function(username) { - return Promise.resolve({'username':'dave','permissions':'read'}); - }, - authenticate: function(username,password) { - authUsername = username; - authPassword = password; - return Promise.resolve({'username':'pete','permissions':'write'}); - } - }); - }); - - describe('#get',function() { - it("returns null for tokenHeader", function() { - should.not.exist(Users.tokenHeader()); - }); - - it('delegates get user',function(done) { - Users.get('dave').then(function(user) { - try { - user.should.have.a.property("username","dave"); - user.should.have.a.property("permissions","read"); - user.should.not.have.a.property("password"); - done(); - } catch(err) { - done(err); - } - }); - }); - it('delegates authenticate user',function(done) { - Users.authenticate('pete','secret').then(function(user) { - try { - user.should.have.a.property("username","pete"); - user.should.have.a.property("permissions","write"); - user.should.not.have.a.property("password"); - authUsername.should.equal('pete'); - authPassword.should.equal('secret'); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initialised with bad settings to test else cases',function() { - before(function() { - Users.init({ - type:"foo", - users:{ - username:"fred", - password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK', - permissions:"*" - } - }); - }); - describe('#get',function() { - it('should fail to return user fred',function(done) { - Users.get("fred").then(function(userf) { - try { - should.not.exist(userf); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initialised with default set as function',function() { - before(function() { - Users.init({ - type:"credentials", - default: function() { return("Done"); } - }); - }); - after(function() { - Users.init({}); - }); - describe('#default',function() { - it('handles api.default being a function',function(done) { - Users.should.have.property('default').which.is.a.Function(); - (Users.default()).should.equal("Done"); - done(); - }); - }); - }); - - describe('Initialised with tokens set as function',function() { - before(function() { - Users.init({ - type:"strategy", - tokens: function(token) { return("Done-"+token); } - }); - }); - after(function() { - Users.init({}); - }); - describe('#tokens',function() { - it('handles api.tokens being a function',function(done) { - Users.should.have.property('tokens').which.is.a.Function(); - (Users.tokens("1234")).should.equal("Done-1234"); - (Users.tokenHeader()).should.equal("authorization"); - done(); - }); - }); - }); - - describe('Initialised with tokens set as function and tokenHeader set as token header name',function() { - before(function() { - Users.init({ - type:"strategy", - tokens: function(token) { return("Done-"+token); }, - tokenHeader: "X-TEST-TOKEN" - }); - }); - after(function() { - Users.init({}); - }); - describe('#tokens',function() { - it('handles api.tokens being a function and api.tokenHeader being a header name',function(done) { - Users.should.have.property('tokens').which.is.a.Function(); - (Users.tokens("1234")).should.equal("Done-1234"); - Users.should.have.property('tokenHeader').which.is.a.Function(); - (Users.tokenHeader()).should.equal("x-test-token"); - done(); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/comms_spec.js b/test/unit/@node-red/editor-api/lib/editor/comms_spec.js deleted file mode 100644 index 4197dc469..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/comms_spec.js +++ /dev/null @@ -1,618 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -const stoppable = require('stoppable'); - -var http = require('http'); -var express = require('express'); -var app = express(); -var WebSocket = require('ws'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var comms = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/comms"); -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); -var Strategies = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/strategies"); -var address = '127.0.0.1'; -var listenPort = 0; // use ephemeral port - - -describe("api/editor/comms", function() { - var connections = []; - var mockComms = { - addConnection: function(opts) { - connections.push(opts.client); - return Promise.resolve() - }, - removeConnection: function(opts) { - for (var i=0;i{}); - return p; - } - } - } - }); - }); - - it('returns stored credentials',function(done) { - request(app) - .get("/credentials/known-type/n1") - .expect("Content-Type",/json/) - .expect(200) - .end(function(err,res) { - if (err) { - done(err); - } else { - try { - res.body.should.have.a.property("user1","abc"); - res.body.should.not.have.a.property("password1"); - res.body.should.have.a.property("has_password1",true); - done(); - } catch(e) { - done(e); - } - } - }) - }); - it('returns any error',function(done) { - request(app) - .get("/credentials/unknown-type/n2") - .expect("Content-Type",/json/) - .expect(400) - .end(function(err,res) { - if (err) { - done(err); - } else { - try { - res.body.should.have.property('code'); - res.body.code.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal('message'); - done(); - } catch(e) { - done(e); - } - } - }) - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/index_spec.js b/test/unit/@node-red/editor-api/lib/editor/index_spec.js deleted file mode 100644 index 8ed4d88f3..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/index_spec.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require("supertest"); -var express = require("express"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var editorApi = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor"); -var comms = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/comms"); -var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings"); -var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; - -describe("api/editor/index", function() { - var app; - describe("disabled the editor", function() { - beforeEach(function() { - sinon.stub(comms,'init').callsFake(function(){}); - sinon.stub(info,'init').callsFake(function(){}); - }); - afterEach(function() { - comms.init.restore(); - info.init.restore(); - }); - it("disables the editor", function() { - var editorApp = editorApi.init({},{disableEditor:true},{}); - should.not.exist(editorApp); - comms.init.called.should.be.false(); - info.init.called.should.be.false(); - }); - }); - describe("enables the editor", function() { - var mockList = [ - 'library','theme','locales','credentials','comms',"settings" - ] - var isStarted = true; - var errors = []; - var session_data = {}; - before(function() { - sinon.stub(auth,'needsPermission').callsFake(function(permission) { - return function(req,res,next) { next(); } - }); - mockList.forEach(function(m) { - sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m),"init").callsFake(function(){}); - }); - sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"),"app").callsFake(function(){ return express()}); - }); - after(function() { - mockList.forEach(function(m) { - NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m).init.restore(); - }) - NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme").app.restore(); - auth.needsPermission.restore(); - log.error.restore(); - }); - - before(function() { - sinon.stub(log,"error").callsFake(function(err) { errors.push(err)}) - app = editorApi.init({},{httpNodeRoot:true, httpAdminRoot: true,disableEditor:false,exportNodeSettings:function(){}},{ - isStarted: () => Promise.resolve(isStarted) - }); - }); - it('serves the editor', function(done) { - request(app) - .get("/") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - // Index page should probably mention Node-RED somewhere - res.text.indexOf("Node-RED").should.not.eql(-1); - done(); - }); - }); - it('serves icons', function(done) { - request(app) - .get("/red/images/icons/arrow-in.svg") - .expect(200) - .expect("Content-Type", /image\/svg\+xml/) - .end(function(err,res) { - done(err); - }); - }); - it('handles page not there', function(done) { - request(app) - .get("/foo") - .expect(404,done) - }); - it('warns if runtime not started', function(done) { - isStarted = false; - request(app) - .get("/") - .expect(503) - .end(function(err,res) { - if (err) { - return done(err); - } - res.text.should.eql("Not started"); - errors.should.have.lengthOf(1); - errors[0].should.eql("Node-RED runtime not started"); - done(); - }); - }); - // it.skip('GET /settings', function(done) { - // request(app).get("/settings").expect(200).end(function(err,res) { - // if (err) { - // return done(err); - // } - // // permissionChecks.should.have.property('settings.read',1); - // done(); - // }) - // }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/library_spec.js b/test/unit/@node-red/editor-api/lib/editor/library_spec.js deleted file mode 100644 index 4a2c281b7..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/library_spec.js +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require('body-parser'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var library = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/library"); - -var app; - -describe("api/editor/library", function() { - - before(function() { - app = express(); - app.use(bodyParser.json()); - app.get(/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,library.getEntry); - app.post(/library\/([^\/]+)\/([^\/]+)\/(.*)/,library.saveEntry); - }); - after(function() { - }); - - it('returns an individual entry - flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve('{"a":1,"b":2}'); - } - } - }); - request(app) - .get('/library/local/flows/abc') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('a',1); - res.body.should.have.property('b',2); - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','abc'); - done(); - }); - }) - it('returns a directory listing - flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve({"a":1,"b":2}); - } - } - }); - request(app) - .get('/library/local/flows/abc/def') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('a',1); - res.body.should.have.property('b',2); - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','abc/def'); - done(); - }); - }) - it('returns an individual entry - non-flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve('{"a":1,"b":2}'); - } - } - }); - request(app) - .get('/library/local/non-flow/abc') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','non-flow'); - opts.should.have.property('path','abc'); - res.text.should.eql('{"a":1,"b":2}'); - done(); - }); - }) - it('returns a directory listing - non-flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve({"a":1,"b":2}); - } - } - }); - request(app) - .get('/library/local/non-flow/abc/def') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('a',1); - res.body.should.have.property('b',2); - opts.should.have.property('library','local'); - opts.should.have.property('type','non-flow'); - opts.should.have.property('path','abc/def'); - done(); - }); - }) - - it('returns an error on individual get', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - var err = new Error("message"); - err.code = "random_error"; - err.status = 400; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .get('/library/local/flows/123') - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','123'); - - res.body.should.have.property('code'); - res.body.code.should.be.equal("random_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("message"); - done(); - }); - }); - - - it('saves an individual entry - flow type', function(done) { - var opts; - library.init({ - library: { - saveEntry: function(_opts) { - opts = _opts; - return Promise.resolve(); - } - } - }); - request(app) - .post('/library/local/flows/abc/def') - .expect(204) - .send({a:1,b:2,c:3}) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','abc/def'); - opts.should.have.property('meta',{}); - opts.should.have.property('body',JSON.stringify({a:1,b:2,c:3})); - done(); - }); - }) - - it('saves an individual entry - non-flow type', function(done) { - var opts; - library.init({ - library: { - saveEntry: function(_opts) { - opts = _opts; - return Promise.resolve(); - } - } - }); - request(app) - .post('/library/local/non-flow/abc/def') - .expect(204) - .send({a:1,b:2,text:"123"}) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','non-flow'); - opts.should.have.property('path','abc/def'); - opts.should.have.property('meta',{a:1,b:2}); - opts.should.have.property('body',"123"); - done(); - }); - }) - - it('returns an error on individual save', function(done) { - var opts; - library.init({ - library: { - saveEntry: function(_opts) { - opts = _opts; - var err = new Error("message"); - err.code = "random_error"; - err.status = 400; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .post('/library/local/non-flow/abc/def') - .send({a:1,b:2,text:"123"}) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('type','non-flow'); - opts.should.have.property('library','local'); - opts.should.have.property('path','abc/def'); - - res.body.should.have.property('code'); - res.body.code.should.be.equal("random_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("message"); - done(); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/locales_spec.js b/test/unit/@node-red/editor-api/lib/editor/locales_spec.js deleted file mode 100644 index fef686a78..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/locales_spec.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var sinon = require('sinon'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var locales = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/locales"); -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("api/editor/locales", function() { - beforeEach(function() { - }) - afterEach(function() { - }) - describe('get named resource catalog',function() { - var app; - before(function() { - // locales.init({ - // i18n: { - // i: { - // language: function() { return 'en-US'}, - // changeLanguage: function(lang,callback) { - // if (callback) { - // callback(); - // } - // }, - // getResourceBundle: function(lang, namespace) { - // return {namespace:namespace, lang:lang}; - // } - // }, - // } - // }); - locales.init({}); - - // bit of a mess of internal workings - sinon.stub(i18n.i,'changeLanguage').callsFake(function(lang,callback) { if (callback) {callback();}}); - if (i18n.i.getResourceBundle) { - sinon.stub(i18n.i,'getResourceBundle').callsFake(function(lang, namespace) {return {namespace:namespace, lang:lang};}); - } else { - // If i18n.init has not been called, then getResourceBundle isn't - // defined - so hardcode a stub - i18n.i.getResourceBundle = function(lang, namespace) {return {namespace:namespace, lang:lang};}; - i18n.i.getResourceBundle.restore = function() { delete i18n.i.getResourceBundle }; - } - app = express(); - app.get(/locales\/(.+)\/?$/,locales.get); - }); - after(function() { - i18n.i.changeLanguage.restore(); - i18n.i.getResourceBundle.restore(); - }) - it('returns with default language', function(done) { - request(app) - .get("/locales/message-catalog") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('namespace','message-catalog'); - done(); - }); - }); - it('returns with selected language', function(done) { - request(app) - .get("/locales/message-catalog?lng=fr-FR") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('namespace','message-catalog'); - res.body.should.have.property('lang','fr-FR'); - done(); - }); - }); - - it('returns for locale defined only with primary tag ', function(done) { - var orig = i18n.i.getResourceBundle; - i18n.i.getResourceBundle = function (lang, ns) { - if (lang === "ja-JP") { - return undefined; - } - return orig(lang, ns); - }; - request(app) - // returns `ja` instead of `ja-JP` - .get("/locales/message-catalog?lng=ja-JP") - .expect(200) - .end(function(err,res) { - i18n.i.getResourceBundle = orig; - if (err) { - return done(err); - } - res.body.should.have.property('namespace','message-catalog'); - res.body.should.have.property('lang','ja'); - done(); - }); - }); - - }); - - // describe('get all node resource catalogs',function() { - // var app; - // before(function() { - // // bit of a mess of internal workings - // sinon.stub(i18n,'catalog').callsFake(function(namespace, lang) { - // return { - // "node-red": "should not return", - // "test-module-a-id": "test-module-a-catalog", - // "test-module-b-id": "test-module-b-catalog", - // "test-module-c-id": "test-module-c-catalog" - // }[namespace] - // }); - // locales.init({ - // nodes: { - // getNodeList: function(opts) { - // return Promise.resolve([ - // {module:"node-red",id:"node-red-id"}, - // {module:"test-module-a",id:"test-module-a-id"}, - // {module:"test-module-b",id:"test-module-b-id"} - // ]); - // } - // } - // }); - // app = express(); - // app.get("/locales/nodes",locales.getAllNodes); - // }); - // after(function() { - // i18n.catalog.restore(); - // }) - // it('returns with the node catalogs', function(done) { - // request(app) - // .get("/locales/nodes") - // .expect(200) - // .end(function(err,res) { - // if (err) { - // return done(err); - // } - // res.body.should.eql({ - // 'test-module-a-id': 'test-module-a-catalog', - // 'test-module-b-id': 'test-module-b-catalog' - // }); - // done(); - // }); - // }); - // }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/projects_spec.js b/test/unit/@node-red/editor-api/lib/editor/projects_spec.js deleted file mode 100644 index 52b88d0b8..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/projects_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("api/editor/projects", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/settings_spec.js b/test/unit/@node-red/editor-api/lib/editor/settings_spec.js deleted file mode 100644 index 171dca564..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/settings_spec.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require("body-parser"); -var sinon = require('sinon'); - -var app; - -var NR_TEST_UTILS = require("nr-test-utils"); - -var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings"); -var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); - -describe("api/editor/settings", function() { - before(function() { - sinon.stub(theme,"settings").callsFake(function() { return { existing: 123, test: 456 };}); - app = express(); - app.use(bodyParser.json()); - app.get("/settings/user",function(req,res,next) {req.user = "fred"; next()}, info.userSettings); - app.post("/settings/user",function(req,res,next) {req.user = "fred"; next()},info.updateUserSettings); - }); - - after(function() { - theme.settings.restore(); - }); - - it('returns the user settings', function(done) { - info.init({ - settings: { - getUserSettings: function(opts) { - if (opts.user !== "fred") { - return Promise.reject(new Error("Unknown user")); - } - return Promise.resolve({ - c:3, - d:4 - }) - } - } - }); - request(app) - .get("/settings/user") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.eql({c:3,d:4}); - done(); - }); - }); - it('updates the user settings', function(done) { - var update; - info.init({ - settings: { - updateUserSettings: function(opts) { - if (opts.user !== "fred") { - return Promise.reject(new Error("Unknown user")); - } - update = opts.settings; - return Promise.resolve() - } - } - }); - request(app) - .post("/settings/user") - .send({ - e:4, - f:5 - }) - .expect(204) - .end(function(err,res) { - if (err) { - return done(err); - } - update.should.eql({e:4,f:5}); - done(); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js b/test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js deleted file mode 100644 index 1647cd99d..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require("supertest"); -var express = require("express"); -var NR_TEST_UTILS = require("nr-test-utils"); -var sshkeys = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/sshkeys"); -var bodyParser = require("body-parser"); - - -describe("api/editor/sshkeys", function() { - var app; - var mockRuntime = { - settings: { - getUserKeys: function() {}, - getUserKey: function() {}, - generateUserKey: function() {}, - removeUserKey: function() {} - } - } - before(function() { - sshkeys.init(mockRuntime); - app = express(); - app.use(bodyParser.json()); - app.use("/settings/user/keys", sshkeys.app()); - }); - - beforeEach(function() { - sinon.stub(mockRuntime.settings, "getUserKeys"); - sinon.stub(mockRuntime.settings, "getUserKey"); - sinon.stub(mockRuntime.settings, "generateUserKey"); - sinon.stub(mockRuntime.settings, "removeUserKey"); - }) - afterEach(function() { - mockRuntime.settings.getUserKeys.restore(); - mockRuntime.settings.getUserKey.restore(); - mockRuntime.settings.generateUserKey.restore(); - mockRuntime.settings.removeUserKey.restore(); - }) - - it('GET /settings/user/keys --- return empty list', function(done) { - mockRuntime.settings.getUserKeys.returns(Promise.resolve([])); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - res.body.keys.should.be.empty(); - done(); - }); - }); - - it('GET /settings/user/keys --- return normal list', function(done) { - var fileList = [ - 'test_key01', - 'test_key02' - ]; - var retList = fileList.map(function(elem) { - return { - name: elem - }; - }); - mockRuntime.settings.getUserKeys.returns(Promise.resolve(retList)); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - for (var item of retList) { - res.body.keys.should.containEql(item); - } - done(); - }); - }); - - it('GET /settings/user/keys --- return Error', function(done) { - var errInstance = new Error("Messages here....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKeys.returns(p); - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return 404', function(done) { - var errInstance = new Error("Not Found."); - errInstance.code = "not_found"; - errInstance.status = 404; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKey.returns(p); - request(app) - .get("/settings/user/keys/NOT_REAL") - .expect(404) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - it('GET /settings/user/keys --- return Unexpected Error', function(done) { - var errInstance = new Error(); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKeys.returns(p) - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return content', function(done) { - var key_file_name = "test_key"; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - mockRuntime.settings.getUserKey.returns(Promise.resolve(fileContent)); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - mockRuntime.settings.getUserKey.called.should.be.true(); - mockRuntime.settings.getUserKey.firstCall.args[0].should.eql({ user: undefined, id: 'test_key' }); - res.body.should.be.deepEqual({ publickey: fileContent }); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("Messages....."); - done(); - }); - }); - - it('POST /settings/user/keys --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.settings.generateUserKey.returns(Promise.resolve(key_file_name)); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - - it('POST /settings/user/keys --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.generateUserKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('POST /settings/user/keys --- return Unexpected error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.generateUserKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("Messages....."); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.settings.removeUserKey.returns(Promise.resolve(true)); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(204) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.be.deepEqual({}); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.removeUserKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.removeUserKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal('Messages.....'); - done(); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/theme_spec.js b/test/unit/@node-red/editor-api/lib/editor/theme_spec.js deleted file mode 100644 index 900be126f..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/theme_spec.js +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require("supertest"); -var express = require('express'); -var sinon = require('sinon'); -var fs = require("fs"); - -var app = express(); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); - -describe("api/editor/theme", function () { - beforeEach(function () { - sinon.stub(fs, "statSync").callsFake(function () { return true; }); - }); - afterEach(function () { - theme.init({settings: {}}); - fs.statSync.restore(); - }); - it("applies the default theme", async function () { - var result = theme.init({}); - should.not.exist(result); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("title", "Node-RED"); - context.page.should.have.a.property("favicon", "favicon.ico"); - context.page.should.have.a.property("tabicon"); - context.page.tabicon.should.have.a.property("icon", "red/images/node-red-icon-black.svg"); - context.page.tabicon.should.have.a.property("colour", "#8f0000"); - context.should.have.a.property("header"); - context.header.should.have.a.property("title", "Node-RED"); - context.header.should.have.a.property("image", "red/images/node-red.svg"); - context.should.have.a.property("asset"); - context.asset.should.have.a.property("red", "red/red.min.js"); - context.asset.should.have.a.property("main", "red/main.min.js"); - context.asset.should.have.a.property("vendorMonaco", ""); - - should.not.exist(theme.settings()); - }); - - it("uses non-minified js files when in dev mode", async function () { - const previousEnv = process.env.NODE_ENV; - try { - process.env.NODE_ENV = 'development' - theme.init({}); - var context = await theme.context(); - context.asset.should.have.a.property("red", "red/red.js"); - context.asset.should.have.a.property("main", "red/main.js"); - } finally { - process.env.NODE_ENV = previousEnv; - } - }); - - it("Adds monaco bootstrap when enabled", async function () { - theme.init({ - editorTheme: { - codeEditor: { - lib: 'monaco' - } - } - }); - var context = await theme.context(); - context.asset.should.have.a.property("vendorMonaco", "vendor/monaco/monaco-bootstrap.js"); - }); - - it("picks up custom theme", async function () { - theme.init({ - editorTheme: { - page: { - title: "Test Page Title", - favicon: "/absolute/path/to/theme/favicon", - tabicon: { - icon: "/absolute/path/to/theme/tabicon", - colour: "#8f008f" - }, - css: [ - "/absolute/path/to/custom/css/file.css" - ], - scripts: "/absolute/path/to/script.js" - }, - header: { - title: "Test Header Title", - url: "http://nodered.org", - image: "/absolute/path/to/header/image" // or null to remove image - }, - - deployButton: { - type: "simple", - label: "Save", - icon: "/absolute/path/to/deploy/button/image" // or null to remove image - }, - - menu: { // Hide unwanted menu items by id. see editor/js/main.js:loadEditor for complete list - "menu-item-import-library": false, - "menu-item-export-library": false, - "menu-item-keyboard-shortcuts": false, - "menu-item-help": { - label: "Alternative Help Link Text", - url: "http://example.com" - } - }, - - userMenu: false, // Hide the user-menu even if adminAuth is enabled - - login: { - image: "/absolute/path/to/login/page/big/image" // a 256x256 image - }, - - palette: { - editable: true, - catalogues: ['https://catalogue.nodered.org/catalogue.json'], - theme: [{ category: ".*", type: ".*", color: "#f0f" }] - }, - - projects: { - enabled: false - } - } - }); - - theme.app(); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("title", "Test Page Title"); - context.page.should.have.a.property("favicon", "theme/favicon/favicon"); - context.page.should.have.a.property("tabicon") - context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon"); - context.page.tabicon.should.have.a.property("colour", "#8f008f") - context.should.have.a.property("header"); - context.header.should.have.a.property("title", "Test Header Title"); - context.header.should.have.a.property("url", "http://nodered.org"); - context.header.should.have.a.property("image", "theme/header/image"); - context.page.should.have.a.property("css"); - context.page.css.should.have.lengthOf(1); - context.page.css[0].should.eql('theme/css/file.css'); - context.page.should.have.a.property("scripts"); - context.page.scripts.should.have.lengthOf(1); - context.page.scripts[0].should.eql('theme/scripts/script.js'); - context.should.have.a.property("login"); - context.login.should.have.a.property("image", "theme/login/image"); - - var settings = theme.settings(); - settings.should.have.a.property("deployButton"); - settings.deployButton.should.have.a.property("type", "simple"); - settings.deployButton.should.have.a.property("label", "Save"); - settings.deployButton.should.have.a.property("icon", "theme/deploy/image"); - settings.should.have.a.property("userMenu"); - settings.userMenu.should.be.eql(false); - settings.should.have.a.property("menu"); - settings.menu.should.have.a.property("menu-item-import-library", false); - settings.menu.should.have.a.property("menu-item-export-library", false); - settings.menu.should.have.a.property("menu-item-keyboard-shortcuts", false); - settings.menu.should.have.a.property("menu-item-help", { label: "Alternative Help Link Text", url: "http://example.com" }); - settings.should.have.a.property("palette"); - settings.palette.should.have.a.property("editable", true); - settings.palette.should.have.a.property("catalogues", ['https://catalogue.nodered.org/catalogue.json']); - settings.palette.should.have.a.property("theme", [{ category: ".*", type: ".*", color: "#f0f" }]); - settings.should.have.a.property("projects"); - settings.projects.should.have.a.property("enabled", false); - }); - - it("picks up backwards compatible tabicon setting", async function () { - theme.init({ - editorTheme: { - page: { - tabicon: "/absolute/path/to/theme/tabicon", - } - } - }); - - theme.app(); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("tabicon"); - context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon"); - // The colour property should remain as default in this case as the - // legacy format for defining tabicon doesn't allow specifying a colour - context.page.tabicon.should.have.a.property("colour", "#8f0000"); - - }); - - it("test explicit userMenu set to true in theme setting", function () { - theme.init({ - editorTheme: { - userMenu: true, - } - }); - - theme.app(); - - var settings = theme.settings(); - settings.should.have.a.property("userMenu"); - settings.userMenu.should.be.eql(true); - - }); - - - it("includes list of plugin themes", function(done) { - theme.init({},{ - plugins: { getPluginsByType: _ => [{id:"theme-plugin"}] } - }); - const app = theme.app(); - request(app) - .get("/") - .end(function(err,res) { - if (err) { - return done(err); - } - try { - const response = JSON.parse(res.text); - response.should.have.property("themes"); - response.themes.should.eql(["theme-plugin"]) - done(); - } catch(err) { - done(err); - } - }); - }); - - it("includes theme plugin settings", async function () { - theme.init({ - editorTheme: { - theme: 'test-theme' - } - },{ - plugins: { getPlugin: t => { - return ({'test-theme':{ - path: '/abosolute/path/to/plugin', - css: [ - "path/to/custom/css/file1.css", - "/invalid/path/to/file2.css", - "../another/invalid/path/file3.css" - ], - scripts: [ - "path/to/custom/js/file1.js", - "/invalid/path/to/file2.js", - "../another/invalid/path/file3.js" - ] - }})[t.id]; - } } - }); - - theme.app(); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("css"); - context.page.css.should.have.lengthOf(1); - context.page.css[0].should.eql('theme/css/file1.css'); - context.page.should.have.a.property("scripts"); - context.page.scripts.should.have.lengthOf(1); - context.page.scripts[0].should.eql('theme/scripts/file1.js'); - - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/ui_spec.js b/test/unit/@node-red/editor-api/lib/editor/ui_spec.js deleted file mode 100644 index 0380adcde..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/ui_spec.js +++ /dev/null @@ -1,210 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require("supertest"); -var express = require("express"); -var fs = require("fs"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var ui = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/ui"); - - -describe("api/editor/ui", function() { - var app; - - before(function() { - ui.init({ - nodes: { - getIcon: function(opts) { - return new Promise(function(resolve,reject) { - if (opts.icon === "icon.png") { - fs.readFile(NR_TEST_UTILS.resolve("@node-red/editor-client/src/images/icons/arrow-in.svg"), function(err,data) { - resolve(data); - }) - } else { - resolve(null); - } - }); - }, - getModuleResource: async function(opts) { - if (opts.module !== "test-module" || opts.path !== "a/path/text.txt") { - return null; - } else { - return "Some text data"; - } - } - } - }); - }); - describe("slash handler", function() { - before(function() { - app = express(); - app.get("/foo",ui.ensureSlash,function(req,res) { res.sendStatus(200);}); - }); - it('redirects if the path does not end in a slash',function(done) { - request(app) - .get('/foo') - .expect(301,done); - }); - it('redirects if the path, with query string, does not end in a slash',function(done) { - request(app) - .get('/foo?abc=def') - .expect(301) - .end(function(err,res) { - if (err) { - return done(err); - } - res.header['location'].should.equal("/foo/?abc=def"); - done(); - }); - }); - - it('does not redirect if the path ends in a slash',function(done) { - request(app) - .get('/foo/') - .expect(200,done); - }); - }); - - describe("icon handler", function() { - before(function() { - app = express(); - app.get("/icons/:module/:icon",ui.icon); - }); - - function binaryParser(res, callback) { - res.setEncoding('binary'); - res.data = ''; - res.on('data', function (chunk) { - res.data += chunk; - }); - res.on('end', function () { - callback(null, Buffer.from(res.data, 'binary')); - }); - } - function compareBuffers(b1,b2) { - b1.length.should.equal(b2.length); - for (var i=0;i layer.name === 'testMiddleware') - should(middlewareFound).be.empty(); - done(); - }); - - it('only accepts functions as middleware',function(done) { - const testMiddleware = function(req, res, next){ next(); }; - api.init({ httpAdminRoot: true, httpAdminMiddleware: testMiddleware },{},{},{}); - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware') - should(middlewareFound).be.length(1); - done(); - }); - }); - - describe('initialises api with authentication enabled', function(done) { - - it('enables an oauth/openID based authentication mechanism',function(done) { - const stub = sinon.stub(apiAuth, 'genericStrategy').callsFake(function(){}); - const adminAuth = { type: 'strategy', strategy: {} } - api.init({ httpAdminRoot: true, adminAuth },{},{},{}); - should(stub.called).be.ok(); - stub.restore(); - done(); - }); - - it('enables password protection',function(done) { - const adminAuth = { type: 'credentials' } - api.init({ httpAdminRoot: true, adminAuth },{},{},{}); - - // is the name ("initialize") of the passport middleware present - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'initialize') - should(middlewareFound).be.length(1); - done(); - }); - - }); - - describe('initialises api with custom cors config', function (done) { - const httpAdminCors = { - origin: "*", - methods: "GET,PUT,POST,DELETE" - }; - - it('uses default cors middleware when user settings absent', function(done){ - api.init({ httpAdminRoot: true }, {}, {}, {}); - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'corsMiddleware') - should(middlewareFound).be.length(1); - done(); - }) - - it('enables custom cors middleware when settings present', function(done){ - api.init({ httpAdminRoot: true, httpAdminCors }, {}, {}, {}); - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'corsMiddleware') - should(middlewareFound).be.length(2); - done(); - }) - }); - - describe('editor start', function (done) { - - it('cannot be started when editor is disabled', function (done) { - const stub = sinon.stub(apiEditor, 'start').callsFake(function () { - return Promise.resolve(true); - }); - api.init({ httpAdminRoot: true, disableEditor: true }, {}, {}, {}); - should(api.start()).resolvedWith(true); - stub.restore(); - done(); - }); - - it('can be started when editor enabled', function (done) { - const stub = sinon.stub(apiEditor, 'start'); - api.init({ httpAdminRoot: true, disableEditor: false }, {}, {}, {}); - api.start(); - should(stub.called).be.true(); - stub.restore(); - done(); - }); - - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/util_spec.js b/test/unit/@node-red/editor-api/lib/util_spec.js deleted file mode 100644 index 26cd76be8..000000000 --- a/test/unit/@node-red/editor-api/lib/util_spec.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require('supertest'); -var express = require('express'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var apiUtil = NR_TEST_UTILS.require("@node-red/editor-api/lib/util"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("api/util", function() { - describe("errorHandler", function() { - var loggedError = null; - var loggedEvent = null; - var app; - before(function() { - app = express(); - sinon.stub(log,'error').callsFake(function(msg) {loggedError = msg;}); - sinon.stub(log,'audit').callsFake(function(event) {loggedEvent = event;}); - app.get("/tooLarge", function(req,res) { - var err = new Error(); - err.message = "request entity too large"; - throw err; - },apiUtil.errorHandler) - app.get("/stack", function(req,res) { - var err = new Error(); - err.message = "stacktrace"; - throw err; - },apiUtil.errorHandler) - }); - after(function() { - log.error.restore(); - log.audit.restore(); - }) - beforeEach(function() { - loggedError = null; - loggedEvent = null; - }) - it("logs an error for request entity too large", function(done) { - request(app).get("/tooLarge").expect(400).end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("error","unexpected_error"); - res.body.should.have.property("message","Error: request entity too large"); - - loggedError.should.have.property("message","request entity too large"); - - loggedEvent.should.have.property("event","api.error"); - loggedEvent.should.have.property("error","unexpected_error"); - loggedEvent.should.have.property("message","Error: request entity too large"); - done(); - }); - }) - it("logs an error plus stack for other errors", function(done) { - request(app).get("/stack").expect(400).end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("error","unexpected_error"); - res.body.should.have.property("message","Error: stacktrace"); - - /Error: stacktrace\s*at.*util_spec.js/m.test(loggedError).should.be.true(); - - loggedEvent.should.have.property("event","api.error"); - loggedEvent.should.have.property("error","unexpected_error"); - loggedEvent.should.have.property("message","Error: stacktrace"); - - - - done(); - }); - }); - }) - - describe('determineLangFromHeaders', function() { - var oldDefaultLang; - before(function() { - oldDefaultLang = i18n.defaultLang; - i18n.defaultLang = "en-US"; - }) - after(function() { - i18n.defaultLang = oldDefaultLang; - }) - it('returns the default lang if non provided', function() { - apiUtil.determineLangFromHeaders(null).should.eql("en-US"); - }) - it('returns the first language accepted', function() { - apiUtil.determineLangFromHeaders(['fr-FR','en-GB']).should.eql("fr-FR"); - }) - }) -}); diff --git a/test/unit/@node-red/registry/lib/deprecated_spec.js b/test/unit/@node-red/registry/lib/deprecated_spec.js deleted file mode 100644 index b9c35dd31..000000000 --- a/test/unit/@node-red/registry/lib/deprecated_spec.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var deprecated = NR_TEST_UTILS.require("@node-red/registry/lib/deprecated.js"); - -describe('deprecated', function() { - it('should return info on a node',function() { - deprecated.get("irc in").should.eql({module:"node-red-node-irc"}); - }); - it('should return null for non-deprecated node',function() { - should.not.exist(deprecated.get("foo")); - }); -}); diff --git a/test/unit/@node-red/registry/lib/externalModules_spec.js b/test/unit/@node-red/registry/lib/externalModules_spec.js deleted file mode 100644 index 2158f93f7..000000000 --- a/test/unit/@node-red/registry/lib/externalModules_spec.js +++ /dev/null @@ -1,371 +0,0 @@ - // init: init, - // register: register, - // registerSubflow: registerSubflow, - // checkFlowDependencies: checkFlowDependencies, - // require: requireModule - // - -const should = require("should"); -const sinon = require("sinon"); -const fs = require("fs-extra"); -const path = require("path"); -const os = require("os"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const externalModules = NR_TEST_UTILS.require("@node-red/registry/lib/externalModules"); -const exec = NR_TEST_UTILS.require("@node-red/util/lib/exec"); -const hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); - -let homeDir; - -async function createUserDir() { - if (!homeDir) { - homeDir = path.join(os.tmpdir(),"nr-test-"+Math.floor(Math.random()*100000)); - } - await fs.ensureDir(homeDir); -} - -async function setupExternalModulesPackage(dependencies) { - await fs.writeFile(path.join(homeDir,"package.json"),`{ -"name": "Node-RED-External-Modules", -"description": "These modules are automatically installed by Node-RED to use in Function nodes.", -"version": "1.0.0", -"private": true, -"dependencies": ${JSON.stringify(dependencies)} -}`) -} - -describe("externalModules api", function() { - beforeEach(async function() { - await createUserDir() - }) - afterEach(async function() { - hooks.clear(); - await fs.remove(homeDir); - }) - describe("checkFlowDependencies", function() { - beforeEach(function() { - sinon.stub(exec,"run").callsFake(async function(cmd, args, options) { - let error; - let moduleName = args[args.length-1]; - if (moduleName === "moduleNotFound") { - error = new Error(); - error.stderr = "E404"; - } else if (moduleName === "moduleVersionNotFound") { - error = new Error(); - error.stderr = "ETARGET"; - } else if (moduleName === "moduleFail") { - error = new Error(); - error.stderr = "Some unexpected install error"; - } - if (error) { - throw error; - } - }) - }) - afterEach(function() { - exec.run.restore(); - }) - it("does nothing when no types are registered",async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.false(); - }); - - it("skips install for modules already installed", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - await setupExternalModulesPackage({"foo": "1.2.3", "bar":"2.3.4"}); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.false(); - }) - - it("skips install for built-in modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "fs"}]} - ]) - exec.run.called.should.be.false(); - }) - - it("installs missing modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.true(); - }) - - - it("calls pre/postInstall hooks", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - let receivedPreEvent,receivedPostEvent; - hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; }) - hooks.add("postInstall", function(event) { receivedPostEvent = event; }) - - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.true(); - // exec.run.lastCall.args[1].should.eql([ 'install', 'a', 'foo' ]); - receivedPreEvent.should.have.property("module","foo") - receivedPreEvent.should.have.property("version") - receivedPreEvent.should.have.property("dir") - receivedPreEvent.should.eql(receivedPostEvent) - }) - - it("skips npm install if preInstall returns false", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - let receivedPreEvent,receivedPostEvent; - hooks.add("preInstall", function(event) { receivedPreEvent = event; return false }) - hooks.add("postInstall", function(event) { receivedPostEvent = event; }) - - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.false(); - receivedPreEvent.should.have.property("module","foo") - receivedPreEvent.should.have.property("version") - receivedPreEvent.should.have.property("dir") - receivedPreEvent.should.eql(receivedPostEvent) - }) - - - it("installs missing modules from inside subflow module", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - externalModules.registerSubflow("sf", {"flow":[{type: "function", libs:[{module: "foo"}]}]}); - await externalModules.checkFlowDependencies([ - {type: "sf"} - ]) - exec.run.called.should.be.true(); - }) - - it("reports install fail - 404", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleNotFound"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleNotFound"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code",404); - - } - }) - it("reports install fail - target", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleVersionNotFound"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleVersionNotFound"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code",404); - } - }) - - it("reports install fail - unexpected", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleFail"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleFail"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","unexpected_error"); - } - }) - it("reports install fail - multiple", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleNotFound"},{module: "moduleFail"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(2); - // Sort the array so we know the order to test for - err.sort(function(A,B) { - return A.module.module.localeCompare(B.module.module); - }) - err[1].should.have.property("module"); - err[1].module.should.have.property("module","moduleNotFound"); - err[1].should.have.property("error"); - err[1].error.should.have.property("code",404); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleFail"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","unexpected_error"); - - } - }) - it("reports install fail - install disabled", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - allowInstall: false - } - }}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - // Should not try to install - exec.run.called.should.be.false(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","foo"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","install_not_allowed"); - } - }) - - it("reports install fail - module disallowed", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['foo'] - } - }}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - // foo disallowed - // bar allowed - {type: "function", libs:[{module: "foo"},{module: "bar"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.calledOnce.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","foo"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","install_not_allowed"); - } - }) - - it("reports install fail - built-in module disallowed", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['fs'] - } - }}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - // foo disallowed - // bar allowed - {type: "function", libs:[{module: "fs"},{module: "bar"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.calledOnce.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","fs"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","module_not_allowed"); - } - }) - }) - describe("require", async function() { - it("requires built-in modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - const result = externalModules.require("fs") - result.should.eql(require("fs")); - }) - it("rejects unknown modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - try { - externalModules.require("foo") - throw new Error("require did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - - it("rejects disallowed modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['fs'] - } - }}); - try { - externalModules.require("fs") - throw new Error("require did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - }) - describe("import", async function() { - it("import built-in modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - const result = await externalModules.import("fs") - // `result` won't have the `should` property - should.exist(result); - should.exist(result.existsSync); - }) - it("rejects unknown modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - try { - await externalModules.import("foo") - throw new Error("import did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - - it("rejects disallowed modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['fs'] - } - }}); - try { - await externalModules.import("fs") - throw new Error("import did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - }) -}); diff --git a/test/unit/@node-red/registry/lib/index_spec.js b/test/unit/@node-red/registry/lib/index_spec.js deleted file mode 100644 index db0629225..000000000 --- a/test/unit/@node-red/registry/lib/index_spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var fs = require("fs"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var registry = NR_TEST_UTILS.require("@node-red/registry"); - -var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer"); -var loader = NR_TEST_UTILS.require("@node-red/registry/lib/loader"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); - -describe('red/registry/index', function() { - var stubs = []; - afterEach(function() { - while(stubs.length) { - stubs.pop().restore(); - } - }) - describe('#init',function() { - it('intialises components', function() { - stubs.push(sinon.stub(installer,"init")); - stubs.push(sinon.stub(loader,"init")); - stubs.push(sinon.stub(typeRegistry,"init")); - - registry.init({settings:{}}); - installer.init.called.should.be.true(); - loader.init.called.should.be.true(); - typeRegistry.init.called.should.be.true(); - }) - }); - - describe('#addModule', function() { - it('loads the module and returns its info', function(done) { - stubs.push(sinon.stub(loader,"addModule").callsFake(function(module) { - return Promise.resolve(); - })); - stubs.push(sinon.stub(typeRegistry,"getModuleInfo").callsFake(function(module) { - return "info"; - })); - registry.addModule("foo").then(function(info) { - info.should.eql("info"); - done(); - }).catch(function(err) { done(err); }); - }); - it('rejects if loader rejects', function(done) { - stubs.push(sinon.stub(loader,"addModule").callsFake(function(module) { - return Promise.reject("error"); - })); - stubs.push(sinon.stub(typeRegistry,"getModuleInfo").callsFake(function(module) { - return "info"; - })); - registry.addModule("foo").then(function(info) { - done(new Error("unexpected resolve")); - }).catch(function(err) { - err.should.eql("error"); - done(); - }) - }); - }); - - describe('#enableNode',function() { - it('enables a node set',function(done) { - stubs.push(sinon.stub(typeRegistry,"enableNodeSet").callsFake(function() { - return Promise.resolve(); - })); - stubs.push(sinon.stub(typeRegistry,"getNodeInfo").callsFake(function() { - return {id:"node-set",loaded:true}; - })); - registry.enableNode("node-set").then(function(ns) { - typeRegistry.enableNodeSet.called.should.be.true(); - ns.should.have.a.property('id','node-set'); - done(); - }).catch(function(err) { done(err); }); - }); - - it('rejects if node unknown',function() { - stubs.push(sinon.stub(typeRegistry,"enableNodeSet").callsFake(function() { - throw new Error('failure'); - })); - /*jshint immed: false */ - (function(){ - registry.enableNode("node-set") - }).should.throw(); - }); - - it('triggers a node load',function(done) { - stubs.push(sinon.stub(typeRegistry,"enableNodeSet").callsFake(function() { - return Promise.resolve(); - })); - var calls = 0; - stubs.push(sinon.stub(typeRegistry,"getNodeInfo").callsFake(function() { - // loaded=false on first call, true on subsequent - return {id:"node-set",loaded:(calls++>0)}; - })); - stubs.push(sinon.stub(loader,"loadNodeSet").callsFake(function(){return Promise.resolve();})); - stubs.push(sinon.stub(typeRegistry,"getFullNodeInfo")); - - registry.enableNode("node-set").then(function(ns) { - typeRegistry.enableNodeSet.called.should.be.true(); - loader.loadNodeSet.called.should.be.true(); - ns.should.have.a.property('id','node-set'); - ns.should.have.a.property('loaded',true); - done(); - }).catch(function(err) { done(err); }); - }); - - }); - -}); diff --git a/test/unit/@node-red/registry/lib/installer_spec.js b/test/unit/@node-red/registry/lib/installer_spec.js deleted file mode 100644 index 80b96ecfb..000000000 --- a/test/unit/@node-red/registry/lib/installer_spec.js +++ /dev/null @@ -1,528 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var fs = require('fs-extra'); -var EventEmitter = require('events'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer"); -var registry = NR_TEST_UTILS.require("@node-red/registry/lib/index"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); -const { events, exec, log, hooks } = NR_TEST_UTILS.require("@node-red/util"); - -describe('nodes/registry/installer', function() { - - var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - _: function(msg) { return msg } - } - - var execResponse; - - beforeEach(function() { - sinon.stub(exec,"run").callsFake(() => execResponse || Promise.resolve("")) - installer.init({}) - }); - - afterEach(function() { - execResponse = null; - if (registry.addModule.restore) { - registry.addModule.restore(); - } - if (registry.removeModule.restore) { - registry.removeModule.restore(); - } - if (typeRegistry.removeModule.restore) { - typeRegistry.removeModule.restore(); - } - if (registry.getModuleInfo.restore) { - registry.getModuleInfo.restore(); - } - if (typeRegistry.getModuleInfo.restore) { - typeRegistry.getModuleInfo.restore(); - } - if (typeRegistry.setModulePendingUpdated.restore) { - typeRegistry.setModulePendingUpdated.restore(); - } - if (fs.statSync.restore) { - fs.statSync.restore(); - } - exec.run.restore(); - hooks.clear(); - }); - - describe("installs module", function() { - it("rejects module name that includes version", function(done) { - installer.installModule("module@version",null,null).catch(function(err) { - err.code.should.be.eql('invalid_module_name'); - done(); - }).catch(done); - }); - it("rejects missing module name", function(done) { - installer.installModule("",null,null).catch(function(err) { - err.code.should.be.eql('invalid_module_name'); - done(); - }).catch(done); - }); - it("rejects null module name", function(done) { - installer.installModule(null,null,null).catch(function(err) { - err.code.should.be.eql('invalid_module_name'); - done(); - }).catch(done); - }); - it("rejects invalid url", function(done) { - installer.installModule("module",null,"abc").catch(function(err) { - err.code.should.be.eql('invalid_module_url'); - done(); - }); - }); - it("rejects when npm returns a 404", function(done) { - var res = { - code: 1, - stdout:"", - stderr:" 404 this_wont_exist" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - installer.installModule("this_wont_exist").catch(function(err) { - err.should.have.property("code",404); - done(); - }).catch(done); - }); - it("rejects when npm does not find specified version", function(done) { - var res = { - code: 1, - stdout:"", - stderr:" version not found: this_wont_exist@0.1.2" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql(404); - done(); - }).catch(done); - }); - it("rejects when update requested to existing version", function(done) { - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.1").catch(function(err) { - err.code.should.be.eql('module_already_loaded'); - done(); - }).catch(done); - }); - it("rejects when update requested to existing version and url", function(done) { - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.1","https://example/foo-0.1.1.tgz").catch(function(err) { - err.code.should.be.eql('module_already_loaded'); - done(); - }).catch(done); - }); - it("rejects with generic error", function(done) { - var res = { - code: 1, - stdout:"", - stderr:" kaboom!" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - installer.installModule("this_wont_exist").then(function() { - done(new Error("Unexpected success")); - }).catch(err => { - // Expected result - done() - }); - }); - it("succeeds when module is found", function(done) { - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist").then(function(info) { - info.should.eql(nodeInfo); - // commsMessages.should.have.length(1); - // commsMessages[0].topic.should.equal("node/added"); - // commsMessages[0].msg.should.eql(nodeInfo.nodes); - done(); - }).catch(done); - }); - it("rejects when non-existant path is provided", function(done) { - this.timeout(20000); - var resourcesDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule","node_modules","NonExistant")); - installer.installModule(resourcesDir).then(function() { - done(new Error("Unexpected success")); - }).catch(function(err) { - if (err.hasOwnProperty("code")) { - err.code.should.eql(404); - done(); - } - else { - console.log("ERRROR::"+err.toString()+"::"); - err.toString().should.eql("Error: Install failed"); - done(); - } - }); - }); - it("succeeds when path is valid node-red module", function(done) { - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - var resourcesDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule","node_modules","TestNodeModule")); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - installer.installModule(resourcesDir).then(function(info) { - info.should.eql(nodeInfo); - done(); - }).catch(done); - }); - it("succeeds when url is valid node-red module", function(done) { - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist",null,"https://example/foo-0.1.1.tgz").then(function(info) { - info.should.eql(nodeInfo); - done(); - }).catch(done); - }); - - it("triggers preInstall and postInstall hooks", function(done) { - let receivedPreEvent,receivedPostEvent; - hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; }) - hooks.add("postInstall", function(event) { receivedPostEvent = event; }) - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - var res = {code: 0,stdout:"",stderr:""} - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","1.2.3").then(function(info) { - exec.run.called.should.be.true(); - exec.run.lastCall.args[1].should.eql([ 'install', 'a', 'this_wont_exist@1.2.3' ]); - info.should.eql(nodeInfo); - should.exist(receivedPreEvent) - receivedPreEvent.should.have.property("module","this_wont_exist") - receivedPreEvent.should.have.property("version","1.2.3") - receivedPreEvent.should.have.property("dir") - receivedPreEvent.should.have.property("url") - receivedPreEvent.should.have.property("isExisting") - receivedPreEvent.should.have.property("isUpgrade") - receivedPreEvent.should.eql(receivedPostEvent) - done(); - }).catch(done); - }); - - it("fails install if preInstall hook fails", function(done) { - let receivedEvent; - hooks.add("preInstall", function(event) { throw new Error("preInstall-error"); }) - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - - installer.installModule("this_wont_exist","1.2.3").catch(function(err) { - exec.run.called.should.be.false(); - done(); - }).catch(done); - }); - - it("skips invoking npm if preInstall returns false", function(done) { - let receivedEvent; - hooks.add("preInstall", function(event) { return false }) - hooks.add("postInstall", function(event) { receivedEvent = event; }) - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","1.2.3").then(function() { - exec.run.called.should.be.false(); - should.exist(receivedEvent); - done(); - }).catch(done); - }); - - it("rollsback install if postInstall hook fails", function(done) { - hooks.add("postInstall", function(event) { throw new Error("fail"); }) - installer.installModule("this_wont_exist","1.2.3").catch(function(err) { - exec.run.calledTwice.should.be.true(); - exec.run.firstCall.args[1].includes("install").should.be.true(); - exec.run.secondCall.args[1].includes("remove").should.be.true(); - done(); - }).catch(done); - }); - - describe("allowUpdate lists", function() { - it("rejects when update requested with allowUpdate set to false", function(done) { - installer.init({ externalModules: { palette: { allowUpdate: false } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql('update_not_allowed'); - done(); - }).catch(done); - }) - it("succeeds when update requested with module not on denyUpdateList", function(done) { - installer.init({ externalModules: { palette: { denyUpdateList: ['this_wont_exist'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_is_allowed",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_is_allowed","0.1.2").then(function() { - done(); - }).catch(done); - }) - it("rejects when update requested with module on denyUpdateList", function(done) { - installer.init({ externalModules: { palette: { denyUpdateList: ['this_wont_exist'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_is_allowed",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql('update_not_allowed'); - done(); - }).catch(done); - }) - it("succeeds when update requested with module on allowUpdateList", function(done) { - installer.init({ externalModules: { palette: { allowUpdateList: ['this_is_allowed'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_is_allowed",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_is_allowed","0.1.2").then(function() { - done(); - }).catch(done); - }) - it("rejects when update requested with module not on allowUpdateList", function(done) { - installer.init({ externalModules: { palette: { allowUpdateList: ['this_is_allowed'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_wont_exist",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql('update_not_allowed'); - done(); - }).catch(done); - }) - }); - }); - describe("uninstalls module", function() { - it("rejects invalid module names", function(done) { - var promises = []; - var rejectedCount = 0; - - promises.push(installer.uninstallModule("this_wont_exist ").catch(() => {rejectedCount++})); - promises.push(installer.uninstallModule("this_wont_exist;no_it_really_wont").catch(() => {rejectedCount++})); - Promise.all(promises).then(function() { - rejectedCount.should.eql(2); - done(); - }).catch(done); - }); - - it("rejects with generic error", function(done) { - var nodeInfo = [{module:"foo",types:["a"]}]; - var removeModule = sinon.stub(registry,"removeModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - var res = { - code: 1, - stdout:"", - stderr:"error" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - - installer.uninstallModule("this_wont_exist").then(function() { - done(new Error("Unexpected success")); - }).catch(err => { - // Expected result - done() - }); - }); - it("succeeds when module is found", function(done) { - var nodeInfo = [{module:"foo",types:["a"]}]; - var removeModule = sinon.stub(typeRegistry,"removeModule").callsFake(function(md) { - return nodeInfo; - }); - var getModuleInfo = sinon.stub(registry,"getModuleInfo").callsFake(function(md) { - return {nodes:[]}; - }); - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - sinon.stub(fs,"statSync").callsFake(function(fn) { return {}; }); - - installer.uninstallModule("this_wont_exist").then(function(info) { - info.should.eql(nodeInfo); - // commsMessages.should.have.length(1); - // commsMessages[0].topic.should.equal("node/removed"); - // commsMessages[0].msg.should.eql(nodeInfo); - done(); - }).catch(done); - }); - }); -}); diff --git a/test/unit/@node-red/registry/lib/library_spec.js b/test/unit/@node-red/registry/lib/library_spec.js deleted file mode 100644 index 2e0e7e99a..000000000 --- a/test/unit/@node-red/registry/lib/library_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var fs = require("fs"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var library = NR_TEST_UTILS.require("@node-red/registry/lib/library"); - -describe("library api", function() { - it('returns null list when no modules have been registered', function() { - library.init(); - should.not.exist(library.getExampleFlows()); - }); - it('returns null path when module is not known', function() { - library.init(); - should.not.exist(library.getExampleFlowPath('foo','bar')); - }); - - it('returns a valid example path', function(done) { - library.init(); - library.addExamplesDir("test-module",path.resolve(__dirname+'/resources/examples')).then(function() { - try { - var flows = library.getExampleFlows(); - flows.should.deepEqual({"test-module":{"f":["one"]}}); - - var examplePath = library.getExampleFlowPath('test-module','one'); - examplePath.should.eql(path.resolve(__dirname+'/resources/examples/one.json')) - - - library.removeExamplesDir('test-module'); - - try { - should.not.exist(library.getExampleFlows()); - should.not.exist(library.getExampleFlowPath('test-module','one')); - done(); - } catch(err) { - done(err); - } - }catch(err) { - done(err); - } - }); - - }) -}); diff --git a/test/unit/@node-red/registry/lib/loader_spec.js b/test/unit/@node-red/registry/lib/loader_spec.js deleted file mode 100644 index b9da833c8..000000000 --- a/test/unit/@node-red/registry/lib/loader_spec.js +++ /dev/null @@ -1,720 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var fs = require("fs-extra"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var loader = NR_TEST_UTILS.require("@node-red/registry/lib/loader"); - -var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem"); -var registry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); - -var nodes = NR_TEST_UTILS.require("@node-red/registry"); - -var resourcesDir = path.resolve(path.join(__dirname,"resources","local")); - -describe("red/nodes/registry/loader",function() { - var stubs = []; - before(function() { - sinon.stub(localfilesystem,"init"); - }); - after(function() { - localfilesystem.init.restore(); - }); - afterEach(function() { - while(stubs.length) { - stubs.pop().restore(); - } - }) - - describe("#load",function() { - it("load empty set without settings available", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ return {};})); - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return {};})); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return false;}}}); - loader.load(true).then(function() { - localfilesystem.getNodeFiles.called.should.be.true(); - localfilesystem.getNodeFiles.lastCall.args[0].should.be.true(); - registry.saveNodeList.called.should.be.false(); - done(); - }) - }); - it("load empty set with settings available triggers registery save", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ return {};})); - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return {};})); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load(true).then(function() { - registry.saveNodeList.called.should.be.true(); - done(); - }).catch(function(err) { - done(err); - }) - }); - - it("load core node files scanned by lfs - single node single file", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version": "1.2.3", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNode1","TestNode1.js"), - "module": "node-red", - "name": "TestNode1" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode1"); - module.nodes.TestNode1.should.have.property("id","node-red/TestNode1"); - module.nodes.TestNode1.should.have.property("module","node-red"); - module.nodes.TestNode1.should.have.property("name","TestNode1"); - module.nodes.TestNode1.should.have.property("file"); - module.nodes.TestNode1.should.have.property("template"); - module.nodes.TestNode1.should.have.property("enabled",true); - module.nodes.TestNode1.should.have.property("loaded",true); - module.nodes.TestNode1.should.have.property("types"); - module.nodes.TestNode1.types.should.have.a.length(1); - module.nodes.TestNode1.types[0].should.eql('test-node-1'); - module.nodes.TestNode1.should.have.property("config"); - module.nodes.TestNode1.config.should.not.eql(""); - module.nodes.TestNode1.should.have.property("help"); - module.nodes.TestNode1.help.should.have.property("en-US"); - module.nodes.TestNode1.should.have.property("namespace","node-red"); - - nodes.registerType.calledOnce.should.be.true(); - nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode1'); - nodes.registerType.lastCall.args[1].should.eql('test-node-1'); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("load core node files scanned by lfs - ignore html if disableEditor true", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version": "1.2.3", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNode1","TestNode1.js"), - "module": "node-red", - "name": "TestNode1" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{disableEditor: true, available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode1"); - module.nodes.TestNode1.should.have.property("id","node-red/TestNode1"); - module.nodes.TestNode1.should.have.property("module","node-red"); - module.nodes.TestNode1.should.have.property("name","TestNode1"); - module.nodes.TestNode1.should.have.property("file"); - module.nodes.TestNode1.should.have.property("template"); - module.nodes.TestNode1.should.have.property("enabled",true); - module.nodes.TestNode1.should.have.property("loaded",true); - // With disableEditor true, the types property is not populated by the - // html file - but instead is populated as nodes register themselves. - // But for this test, we have stubbed out registerType, so we won't get any types - // module.nodes.TestNode1.should.have.property("types"); - // module.nodes.TestNode1.types.should.have.a.length(1); - // module.nodes.TestNode1.types[0].should.eql('test-node-1'); - - // With disableEditor set, config should be blank - module.nodes.TestNode1.should.have.property("config"); - module.nodes.TestNode1.config.should.eql(""); - - // help should be an empty object - module.nodes.TestNode1.should.have.property("help"); - module.nodes.TestNode1.help.should.eql({}) - module.nodes.TestNode1.should.have.property("namespace","node-red"); - - nodes.registerType.calledOnce.should.be.true(); - nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode1'); - nodes.registerType.lastCall.args[1].should.eql('test-node-1'); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("load core node files scanned by lfs - multiple nodes single file", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version": "4.5.6", - "nodes": { - "MultipleNodes1": { - "file": path.join(resourcesDir,"MultipleNodes1","MultipleNodes1.js"), - "module": "node-red", - "name": "MultipleNodes1" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","4.5.6"); - module.should.have.property("nodes"); - module.nodes.should.have.property("MultipleNodes1"); - module.nodes.MultipleNodes1.should.have.property("id","node-red/MultipleNodes1"); - module.nodes.MultipleNodes1.should.have.property("module","node-red"); - module.nodes.MultipleNodes1.should.have.property("name","MultipleNodes1"); - module.nodes.MultipleNodes1.should.have.property("file"); - module.nodes.MultipleNodes1.should.have.property("template"); - module.nodes.MultipleNodes1.should.have.property("enabled",true); - module.nodes.MultipleNodes1.should.have.property("loaded",true); - module.nodes.MultipleNodes1.should.have.property("types"); - module.nodes.MultipleNodes1.types.should.have.a.length(2); - module.nodes.MultipleNodes1.types[0].should.eql('test-node-multiple-1a'); - module.nodes.MultipleNodes1.types[1].should.eql('test-node-multiple-1b'); - module.nodes.MultipleNodes1.should.have.property("config"); - module.nodes.MultipleNodes1.should.have.property("help"); - module.nodes.MultipleNodes1.should.have.property("namespace","node-red"); - - nodes.registerType.calledTwice.should.be.true(); - nodes.registerType.firstCall.args[0].should.eql('node-red/MultipleNodes1'); - nodes.registerType.firstCall.args[1].should.eql('test-node-multiple-1a'); - nodes.registerType.secondCall.args[0].should.eql('node-red/MultipleNodes1'); - nodes.registerType.secondCall.args[1].should.eql('test-node-multiple-1b'); - - - done(); - }).catch(function(err) { - done(err); - }); - }); - - - it("load core node files scanned by lfs - node with promise", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version":"2.4.6", - "nodes": { - "TestNode2": { - "file": path.join(resourcesDir,"TestNode2","TestNode2.js"), - "module": "node-red", - "name": "TestNode2" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","2.4.6"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode2"); - module.nodes.TestNode2.should.have.property("id","node-red/TestNode2"); - module.nodes.TestNode2.should.have.property("module","node-red"); - module.nodes.TestNode2.should.have.property("name","TestNode2"); - module.nodes.TestNode2.should.have.property("file"); - module.nodes.TestNode2.should.have.property("template"); - module.nodes.TestNode2.should.have.property("enabled",true); - module.nodes.TestNode2.should.have.property("loaded",true); - module.nodes.TestNode2.should.have.property("types"); - module.nodes.TestNode2.types.should.have.a.length(1); - module.nodes.TestNode2.types[0].should.eql('test-node-2'); - module.nodes.TestNode2.should.have.property("config"); - module.nodes.TestNode2.should.have.property("help"); - module.nodes.TestNode2.should.have.property("namespace","node-red"); - module.nodes.TestNode2.should.not.have.property('err'); - - nodes.registerType.calledOnce.should.be.true(); - nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode2'); - nodes.registerType.lastCall.args[1].should.eql('test-node-2'); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - - it("load core node files scanned by lfs - node with rejecting promise", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version":"1.2.3", - "nodes": { - "TestNode3": { - "file": path.join(resourcesDir,"TestNode3","TestNode3.js"), - "module": "node-red", - "name": "TestNode3" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode3"); - module.nodes.TestNode3.should.have.property("id","node-red/TestNode3"); - module.nodes.TestNode3.should.have.property("module","node-red"); - module.nodes.TestNode3.should.have.property("name","TestNode3"); - module.nodes.TestNode3.should.have.property("file"); - module.nodes.TestNode3.should.have.property("template"); - module.nodes.TestNode3.should.have.property("enabled",true); - module.nodes.TestNode3.should.have.property("loaded",false); - module.nodes.TestNode3.should.have.property("types"); - module.nodes.TestNode3.types.should.have.a.length(1); - module.nodes.TestNode3.types[0].should.eql('test-node-3'); - module.nodes.TestNode3.should.have.property("config"); - module.nodes.TestNode3.should.have.property("help"); - module.nodes.TestNode3.should.have.property("namespace","node-red"); - module.nodes.TestNode3.should.have.property('err','fail'); - - nodes.registerType.called.should.be.false(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("load core node files scanned by lfs - missing file", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version":"1.2.3", - "nodes": { - "DoesNotExist": { - "file": path.join(resourcesDir,"doesnotexist"), - "module": "node-red", - "name": "DoesNotExist" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("DoesNotExist"); - module.nodes.DoesNotExist.should.have.property("id","node-red/DoesNotExist"); - module.nodes.DoesNotExist.should.have.property("module","node-red"); - module.nodes.DoesNotExist.should.have.property("name","DoesNotExist"); - module.nodes.DoesNotExist.should.have.property("file"); - module.nodes.DoesNotExist.should.have.property("template"); - module.nodes.DoesNotExist.should.have.property("enabled",true); - module.nodes.DoesNotExist.should.have.property("loaded",false); - module.nodes.DoesNotExist.should.have.property("types"); - module.nodes.DoesNotExist.types.should.have.a.length(0); - module.nodes.DoesNotExist.should.have.property("config",""); - module.nodes.DoesNotExist.should.have.property("help",{}); - module.nodes.DoesNotExist.should.have.property("namespace","node-red"); - module.nodes.DoesNotExist.should.have.property('err'); - - nodes.registerType.called.should.be.false(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - // it("load core node files scanned by lfs - missing html file", function(done) { - // // This is now an okay situation - // stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - // var result = {}; - // result["node-red"] = { - // "name": "node-red", - // "version": "1.2.3", - // "nodes": { - // "DuffNode": { - // "file": path.join(resourcesDir,"DuffNode","DuffNode.js"), - // "module": "node-red", - // "name": "DuffNode" - // } - // } - // }; - // return result; - // })); - // - // stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - // stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // // This module isn't already loaded - // stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - // - // stubs.push(sinon.stub(nodes,"registerType")); - // loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - // loader.load().then(function(result) { - // - // registry.addModule.called.should.be.true(); - // var module = registry.addModule.lastCall.args[0]; - // module.should.have.property("name","node-red"); - // module.should.have.property("version","1.2.3"); - // module.should.have.property("nodes"); - // module.nodes.should.have.property("DuffNode"); - // module.nodes.DuffNode.should.have.property("id","node-red/DuffNode"); - // module.nodes.DuffNode.should.have.property("module","node-red"); - // module.nodes.DuffNode.should.have.property("name","DuffNode"); - // module.nodes.DuffNode.should.have.property("file"); - // module.nodes.DuffNode.should.have.property("template"); - // module.nodes.DuffNode.should.have.property("enabled",true); - // module.nodes.DuffNode.should.have.property("loaded",false); - // module.nodes.DuffNode.should.have.property("types"); - // module.nodes.DuffNode.types.should.have.a.length(0); - // module.nodes.DuffNode.should.have.property("config",""); - // module.nodes.DuffNode.should.have.property("help",{}); - // module.nodes.DuffNode.should.have.property("namespace","node-red"); - // module.nodes.DuffNode.should.have.property('err'); - // module.nodes.DuffNode.err.should.endWith("DuffNode.html does not exist"); - // - // nodes.registerType.called.should.be.false(); - // - // done(); - // }).catch(function(err) { - // done(err); - // }); - // }); - }); - - describe("#addModule",function() { - it("throws error if settings unavailable", function() { - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return false;}}}); - /*jshint immed: false */ - (function(){ - loader.addModule("test-module"); - }).should.throw("Settings unavailable"); - }); - - it("returns rejected error if module already loaded", function(done) { - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){return{}})); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - - loader.addModule("test-module").catch(function(err) { - err.code.should.eql("module_already_loaded"); - done(); - }); - }); - it("returns rejected error if module not found", function(done) { - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){return null})); - stubs.push(sinon.stub(localfilesystem,"getModuleFiles").callsFake(function() { - throw new Error("failure"); - })); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.addModule("test-module").catch(function(err) { - err.message.should.eql("failure"); - done(); - }); - - }); - - it("loads module by name", function(done) { - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(localfilesystem,"getModuleFiles").callsFake(function(){ - var result = {}; - result["TestNodeModule"] = { - "name": "TestNodeModule", - "version": "1.2.3", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNodeModule","node_modules","TestNodeModule","TestNodeModule.js"), - "module": "TestNodeModule", - "name": "TestNode1", - "version": "1.2.3" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return "a node list" })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.addModule("TestNodeModule").then(function(result) { - result.should.eql("TestNodeModule"); - - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","TestNodeModule"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode1"); - module.nodes.TestNode1.should.have.property("id","TestNodeModule/TestNode1"); - module.nodes.TestNode1.should.have.property("module","TestNodeModule"); - module.nodes.TestNode1.should.have.property("name","TestNode1"); - module.nodes.TestNode1.should.have.property("file"); - module.nodes.TestNode1.should.have.property("template"); - module.nodes.TestNode1.should.have.property("enabled",true); - module.nodes.TestNode1.should.have.property("loaded",true); - module.nodes.TestNode1.should.have.property("types"); - module.nodes.TestNode1.types.should.have.a.length(1); - module.nodes.TestNode1.types[0].should.eql('test-node-mod-1'); - module.nodes.TestNode1.should.have.property("config"); - module.nodes.TestNode1.should.have.property("help"); - module.nodes.TestNode1.should.have.property("namespace","TestNodeModule"); - module.nodes.TestNode1.should.not.have.property('err'); - - nodes.registerType.calledOnce.should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("skips module that fails version check", function(done) { - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(localfilesystem,"getModuleFiles").callsFake(function(){ - var result = {}; - result["TestNodeModule"] = { - "name": "TestNodeModule", - "version": "1.2.3", - "redVersion":"999.0.0", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNodeModule","node_modules","TestNodeModule","TestNodeModule.js"), - "module": "TestNodeModule", - "name": "TestNode1", - "version": "1.2.3" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return "a node list" })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({log:{"_":function(){},warn:function(){}},nodes:nodes,version: function() { return "0.12.0"}, settings:{available:function(){return true;}}}); - loader.addModule("TestNodeModule").then(function(result) { - result.should.eql("TestNodeModule"); - registry.addModule.called.should.be.false(); - nodes.registerType.called.should.be.false(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it.skip('registers a message catalog'); - - - }); - describe("#loadNodeSet",function() { - it("no-ops the load if node is not enabled", function(done) { - stubs.push(sinon.stub(nodes,"registerType")); - loader.loadNodeSet({ - "file": path.join(resourcesDir,"TestNode1","TestNode1.js"), - "module": "node-red", - "name": "TestNode1", - "enabled": false - }).then(function(node) { - node.enabled.should.be.false(); - nodes.registerType.called.should.be.false(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("handles node that errors on require", function(done) { - stubs.push(sinon.stub(nodes,"registerType")); - loader.loadNodeSet({ - "file": path.join(resourcesDir,"TestNode4","TestNode4.js"), - "module": "node-red", - "name": "TestNode4", - "enabled": true - }).then(function(node) { - node.enabled.should.be.true(); - nodes.registerType.called.should.be.false(); - node.should.have.property('err'); - node.err.toString().should.eql("Error: fail to require (line:1)"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - describe("#getNodeHelp",function() { - it("returns preloaded help", function() { - loader.getNodeHelp({ - help:{ - en:"foo" - } - },"en").should.eql("foo"); - }); - it("loads help, caching result", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - return 'bar'; - })) - var node = { - template: "/tmp/node/directory/file.html", - help:{ - en:"foo" - } - }; - loader.getNodeHelp(node,"fr").should.eql("bar"); - node.help['fr'].should.eql("bar"); - fs.readFileSync.calledOnce.should.be.true(); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/fr/file.html")); - loader.getNodeHelp(node,"fr").should.eql("bar"); - fs.readFileSync.calledOnce.should.be.true(); - }); - it("loads help, defaulting to en-US content", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - throw new Error("not found"); - })) - var node = { - template: "/tmp/node/directory/file.html", - help:{} - }; - node.help['en-US'] = 'foo'; - - loader.getNodeHelp(node,"fr").should.eql("foo"); - node.help['fr'].should.eql("foo"); - fs.readFileSync.calledOnce.should.be.true(); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/fr/file.html")); - loader.getNodeHelp(node,"fr").should.eql("foo"); - fs.readFileSync.calledOnce.should.be.true(); - }); - it("loads help, defaulting to en-US content for extra nodes", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - if (path.indexOf("en-US") >= 0) { - return 'bar'; - } - throw new Error("not found"); - })); - var node = { - template: "/tmp/node/directory/file.html", - help:{} - }; - delete node.help['en-US']; - - loader.getNodeHelp(node,"fr").should.eql("bar"); - node.help['fr'].should.eql("bar"); - fs.readFileSync.calledTwice.should.be.true(); - fs.readFileSync.firstCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/fr/file.html")); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/en-US/file.html")); - loader.getNodeHelp(node,"fr").should.eql("bar"); - fs.readFileSync.calledTwice.should.be.true(); - }); - it("fails to load en-US help content", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - throw new Error("not found"); - })); - var node = { - template: "/tmp/node/directory/file.html", - help:{} - }; - delete node.help['en-US']; - - should.not.exist(loader.getNodeHelp(node,"en-US")); - should.not.exist(node.help['en-US']); - fs.readFileSync.calledTwice.should.be.true(); - fs.readFileSync.firstCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/en-US/file.html")); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/en/file.html")); - should.not.exist(loader.getNodeHelp(node,"en-US")); - fs.readFileSync.callCount.should.eql(4); - }); - - }); -}); diff --git a/test/unit/@node-red/registry/lib/localfilesystem_spec.js b/test/unit/@node-red/registry/lib/localfilesystem_spec.js deleted file mode 100644 index e84761657..000000000 --- a/test/unit/@node-red/registry/lib/localfilesystem_spec.js +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem"); - -var resourcesDir = path.resolve(path.join(__dirname,"resources","local")); -var userDir = path.resolve(path.join(__dirname,"resources","userDir")); -var moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule")); - -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("red/nodes/registry/localfilesystem",function() { - beforeEach(function() { - stubs.push(sinon.stub(i18n,"registerMessageCatalog").callsFake(function() { return Promise.resolve(); })); - }) - - var stubs = []; - afterEach(function() { - while(stubs.length) { - stubs.pop().restore(); - } - }) - function checkNodes(nodes,shouldHaveNodes,shouldNotHaveNodes,module) { - for (var i=0;i modules[moduleId]); - sinon.stub(registry,"getModuleList").callsFake(() => modules) - }); - afterEach(function() { - events.removeListener("registry:plugin-added",handleEvent); - registry.getModule.restore(); - registry.getModuleList.restore(); - }) - - describe("registerPlugin", function() { - it("registers a plugin", function() { - let pluginDef = {} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - receivedEvents.length.should.eql(1); - receivedEvents[0].should.eql("a-plugin"); - should.exist(modules['test-module'].plugins['test-set'].plugins[0]) - modules['test-module'].plugins['test-set'].plugins[0].should.equal(pluginDef) - }) - it("calls a plugins onadd function", function() { - let pluginDef = { onadd: sinon.stub() } - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - pluginDef.onadd.called.should.be.true(); - }) - }) - - describe("getPlugin", function() { - it("returns a registered plugin", function() { - let pluginDef = {} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - pluginDef.should.equal(plugins.getPlugin("a-plugin")); - }) - }) - describe("getPluginsByType", function() { - it("returns a plugins of a given type", function() { - let pluginDef = {type: "foo"} - let pluginDef2 = {type: "bar"} - let pluginDef3 = {type: "foo"} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2); - plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3); - - let fooPlugins = plugins.getPluginsByType("foo"); - let barPlugins = plugins.getPluginsByType("bar"); - let noPlugins = plugins.getPluginsByType("none"); - - noPlugins.should.be.of.length(0); - - fooPlugins.should.be.of.length(2); - fooPlugins.should.containEql(pluginDef); - fooPlugins.should.containEql(pluginDef3); - - barPlugins.should.be.of.length(1); - barPlugins.should.containEql(pluginDef2); - - }) - }) - - describe("getPluginConfigs", function() { - it("gets all plugin configs", function() { - let configs = plugins.getPluginConfigs("en-US"); - configs.should.eql(` - -test-module-config`) - }) - }) - - - describe("getPluginList", function() { - it("returns a plugins of a given type", function() { - let pluginDef = {type: "foo"} - let pluginDef2 = {type: "bar"} - let pluginDef3 = {type: "foo"} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2); - plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3); - - let pluginList = plugins.getPluginList(); - JSON.stringify(pluginList).should.eql(JSON.stringify( - [ - { - "id": "test-module/test-set", - "enabled": true, - "local": false, - "user": false, - "plugins": [ - { - "type": "foo", - "id": "a-plugin", - "module": "test-module" - }, - { - "type": "bar", - "id": "a-plugin2", - "module": "test-module" - }, - { - "type": "foo", - "id": "a-plugin3", - "module": "test-module" - } - ] - }, - { - "id": "test-module/test-disabled-set", - "enabled": false, - "local": false, - "user": false, - "plugins": [] - } - ] - )) - }) - }) - describe("exportPluginSettings", function() { - it("exports plugin settings - default false", function() { - plugins.init({ "a-plugin": { a: 123, b:234, c: 345} }); - plugins.registerPlugin("test-module/test-set","a-plugin",{ - settings: { - a: { exportable: true }, - b: {exportable: false }, - d: { exportable: true, value: 456} - - } - }); - var exportedSet = {}; - plugins.exportPluginSettings(exportedSet); - exportedSet.should.have.property("a-plugin"); - // a is exportable - exportedSet["a-plugin"].should.have.property("a",123); - // b is explicitly not exportable - exportedSet["a-plugin"].should.not.have.property("b"); - // c isn't listed and default false - exportedSet["a-plugin"].should.not.have.property("c"); - // d has a default value - exportedSet["a-plugin"].should.have.property("d",456); - }) - it("exports plugin settings - default true", function() { - plugins.init({ "a-plugin": { a: 123, b:234, c: 345} }); - plugins.registerPlugin("test-module/test-set","a-plugin",{ - settings: { - '*': { exportable: true }, - a: { exportable: true }, - b: {exportable: false }, - d: { exportable: true, value: 456} - - } - }); - var exportedSet = {}; - plugins.exportPluginSettings(exportedSet); - exportedSet.should.have.property("a-plugin"); - // a is exportable - exportedSet["a-plugin"].should.have.property("a",123); - // b is explicitly not exportable - exportedSet["a-plugin"].should.not.have.property("b"); - // c isn't listed, but default true - exportedSet["a-plugin"].should.have.property("c"); - // d has a default value - exportedSet["a-plugin"].should.have.property("d",456); - }) - }); -}); diff --git a/test/unit/@node-red/registry/lib/registry_spec.js b/test/unit/@node-red/registry/lib/registry_spec.js deleted file mode 100644 index 5501264a7..000000000 --- a/test/unit/@node-red/registry/lib/registry_spec.js +++ /dev/null @@ -1,614 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); -const { events } = NR_TEST_UTILS.require("@node-red/util"); - -describe("red/nodes/registry/registry",function() { - - afterEach(function() { - typeRegistry.clear(); - }); - - function stubSettings(s,available,initialConfig) { - s.available = function() {return available;}; - s.set = sinon.spy(function(s,v) { return Promise.resolve();}); - s.get = function(s) { return initialConfig;}; - return s; - } - - var settings = stubSettings({},false,null); - var settingsWithStorageAndInitialConfig = stubSettings({},true,{"node-red":{module:"testModule",name:"testName",version:"testVersion",nodes:{"node":{id:"node-red/testName",name:"test",types:["a","b"],enabled:true}}}}); - - var testNodeSet1 = { - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"] - }; - - var testNodeSet2 = { - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-c","test-d"] - }; - var testNodeSet2WithError = { - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - err: "I have an error", - config: "configC", - types: [ "test-c","test-d"] - }; - var testNodeSet3 = { - id: "test-module-2/test-name-3", - module: "test-module-2", - name: "test-name-3", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-a","test-e"] - }; - - - - describe('#init/load', function() { - it('loads initial config', function(done) { - typeRegistry.init(settingsWithStorageAndInitialConfig,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.load(); - typeRegistry.getNodeList().should.have.lengthOf(1); - done(); - }); - - it('migrates legacy format', function(done) { - var legacySettings = { - available: function() { return true; }, - set: sinon.stub().returns(Promise.resolve()), - get: function() { return { - "123": { - "name": "72-sentiment.js", - "types": [ - "sentiment" - ], - "enabled": true - }, - "456": { - "name": "20-inject.js", - "types": [ - "inject" - ], - "enabled": true - }, - "789": { - "name": "testModule:a-module.js", - "types": [ - "example" - ], - "enabled":true, - "module":"testModule" - } - }} - }; - var expected = JSON.parse('{"node-red":{"name":"node-red","nodes":{"sentiment":{"name":"sentiment","types":["sentiment"],"enabled":true,"module":"node-red"},"inject":{"name":"inject","types":["inject"],"enabled":true,"module":"node-red"}}},"testModule":{"name":"testModule","nodes":{"a-module.js":{"name":"a-module.js","types":["example"],"enabled":true,"module":"testModule"}}}}'); - typeRegistry.init(legacySettings,null); - typeRegistry.load(); - legacySettings.set.calledOnce.should.be.true(); - legacySettings.set.args[0][1].should.eql(expected); - done(); - }); - }); - - - describe.skip('#addNodeSet', function() { - it('adds a node set for an unknown module', function() { - - typeRegistry.init(settings,null); - - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - - typeRegistry.getNodeList().should.have.lengthOf(1); - var moduleList = typeRegistry.getModuleList(); - moduleList.should.have.a.property("test-module"); - moduleList["test-module"].should.have.a.property("name","test-module"); - moduleList["test-module"].should.have.a.property("version","0.0.1"); - moduleList["test-module"].should.have.a.property("nodes"); - moduleList["test-module"].nodes.should.have.a.property("test-name"); - - moduleList["test-module"].nodes["test-name"].should.eql({ - config: 'configA', - id: 'test-module/test-name', - module: 'test-module', - name: 'test-name', - enabled: true, - loaded: false, - types: [ 'test-a', 'test-b' ] - }); - - }); - - it('adds a node set to an existing module', function() { - - typeRegistry.init(settings,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - - typeRegistry.getNodeList().should.have.lengthOf(1); - var moduleList = typeRegistry.getModuleList(); - Object.keys(moduleList).should.have.a.lengthOf(1); - moduleList.should.have.a.property("test-module"); - moduleList["test-module"].should.have.a.property("name","test-module"); - moduleList["test-module"].should.have.a.property("version","0.0.1"); - moduleList["test-module"].should.have.a.property("nodes"); - - Object.keys(moduleList["test-module"].nodes).should.have.a.lengthOf(1); - moduleList["test-module"].nodes.should.have.a.property("test-name"); - - - typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2); - - typeRegistry.getNodeList().should.have.lengthOf(2); - moduleList = typeRegistry.getModuleList(); - Object.keys(moduleList).should.have.a.lengthOf(1); - Object.keys(moduleList["test-module"].nodes).should.have.a.lengthOf(2); - moduleList["test-module"].nodes.should.have.a.property("test-name"); - moduleList["test-module"].nodes.should.have.a.property("test-name-2"); - }); - - it('doesnt add node set types if node set has an error', function() { - typeRegistry.init(settings,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - - typeRegistry.getTypeId("test-a").should.eql("test-module/test-name"); - - should.not.exist(typeRegistry.getTypeId("test-c")); - - typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2WithError, "0.0.1"); - - should.not.exist(typeRegistry.getTypeId("test-c")); - }); - - it('doesnt add node set if type already exists', function() { - typeRegistry.init(settings,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - should.not.exist(typeRegistry.getTypeId("test-e")); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - typeRegistry.getNodeList().should.have.lengthOf(1); - should.exist(typeRegistry.getTypeId("test-a")); - typeRegistry.addNodeSet(testNodeSet3.id,testNodeSet3, "0.0.1"); - typeRegistry.getNodeList().should.have.lengthOf(2); - - // testNodeSet3 registers a duplicate test-a and unique test-e - // as test-a is a duplicate, test-e should not get registered - should.not.exist(typeRegistry.getTypeId("test-e")); - - var testNodeSet3Result = typeRegistry.getNodeList()[1]; - should.exist(testNodeSet3Result.err); - testNodeSet3Result.err.code.should.equal("type_already_registered"); - testNodeSet3Result.err.details.type.should.equal("test-a"); - testNodeSet3Result.err.details.moduleA.should.equal("test-module"); - testNodeSet3Result.err.details.moduleB.should.equal("test-module-2"); - - // - // typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2WithError, "0.0.1"); - // - // should.not.exist(typeRegistry.getTypeId("test-c")); - }); - - - }); - - describe("#enableNodeSet", function() { - it('throws error if settings unavailable', function() { - typeRegistry.init(settings,null); - /*jshint immed: false */ - (function(){ - typeRegistry.enableNodeSet("test-module/test-name"); - }).should.throw("Settings unavailable"); - }); - - it('throws error if module unknown', function() { - typeRegistry.init(settingsWithStorageAndInitialConfig,null); - /*jshint immed: false */ - (function(){ - typeRegistry.enableNodeSet("test-module/unknown"); - }).should.throw("Unrecognised id: test-module/unknown"); - }); - it.skip('enables the node',function(){}) - - }); - describe("#disableNodeSet", function() { - it('throws error if settings unavailable', function() { - typeRegistry.init(settings,null); - /*jshint immed: false */ - (function(){ - typeRegistry.disableNodeSet("test-module/test-name"); - }).should.throw("Settings unavailable"); - }); - - it('throws error if module unknown', function() { - typeRegistry.init(settingsWithStorageAndInitialConfig,null); - /*jshint immed: false */ - (function(){ - typeRegistry.disableNodeSet("test-module/unknown"); - }).should.throw("Unrecognised id: test-module/unknown"); - }); - it.skip('disables the node',function(){}) - }); - - describe('#getNodeConfig', function() { - it('returns nothing for an unregistered type config', function(done) { - typeRegistry.init(settings,null); - var config = typeRegistry.getNodeConfig("imaginary-shark"); - (config === null).should.be.true(); - done(); - }); - }); - - describe('#saveNodeList',function() { - it('rejects when settings unavailable',function(done) { - typeRegistry.init(stubSettings({},false,{}),null); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {"test-name":{module:"test-module",name:"test-name",types:[]}}}); - typeRegistry.saveNodeList().catch(function(err) { - done(); - }); - }); - it('saves the list',function(done) { - var s = stubSettings({},true,{}); - typeRegistry.init(s,null); - - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":testNodeSet1, - "test-name-2":testNodeSet2WithError - }}); - - typeRegistry.saveNodeList().then(function() { - s.set.called.should.be.true(); - s.set.lastCall.args[0].should.eql('nodes'); - var nodes = s.set.lastCall.args[1]; - nodes.should.have.property('test-module'); - for (var n in nodes['test-module'].nodes) { - if (nodes['test-module'].nodes.hasOwnProperty(n)) { - var nn = nodes['test-module'].nodes[n]; - nn.should.not.have.property('err'); - nn.should.not.have.property('id'); - } - } - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe('#removeModule',function() { - it('throws error for unknown module', function() { - var s = stubSettings({},true,{}); - typeRegistry.init(s,null); - /*jshint immed: false */ - (function(){ - typeRegistry.removeModule("test-module/unknown"); - }).should.throw("Unrecognised module: test-module/unknown"); - }); - it('throws error for unavaiable settings', function() { - var s = stubSettings({},false,{}); - typeRegistry.init(s,null); - /*jshint immed: false */ - (function(){ - typeRegistry.removeModule("test-module/unknown"); - }).should.throw("Settings unavailable"); - }); - it('removes a known module', function() { - var s = stubSettings({},true,{}); - typeRegistry.init(s,null); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":testNodeSet1 - }}); - var moduleList = typeRegistry.getModuleList(); - moduleList.should.have.a.property("test-module"); - typeRegistry.getNodeList().should.have.lengthOf(1); - - var info = typeRegistry.removeModule('test-module'); - moduleList = typeRegistry.getModuleList(); - moduleList.should.not.have.a.property("test-module"); - typeRegistry.getNodeList().should.have.lengthOf(0); - }); - }); - - describe('#get[All]NodeConfigs', function() { - it('returns node config', function() { - typeRegistry.init(settings,{ - getNodeHelp: function(config) { return "HE"+config.name+"LP" } - }); - - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"] - }, - "test-name-2":{ - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-c","test-d"] - } - }}); - typeRegistry.getNodeConfig("test-module/test-name").should.eql('\nconfigAHEtest-nameLP'); - typeRegistry.getNodeConfig("test-module/test-name-2").should.eql('\nconfigBHEtest-name-2LP'); - typeRegistry.getAllNodeConfigs().should.eql('\n\nconfigAHEtest-nameLP\n\nconfigBHEtest-name-2LP'); - }); - }); - describe('#getModuleInfo', function() { - it('returns module info', function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - }}); - var moduleInfo = typeRegistry.getModuleInfo("test-module"); - moduleInfo.should.have.a.property('name','test-module'); - moduleInfo.should.have.a.property('version','0.0.1'); - moduleInfo.should.have.a.property('nodes'); - moduleInfo.nodes.should.have.a.lengthOf(1); - moduleInfo.nodes[0].should.have.a.property('id','test-module/test-name'); - moduleInfo.nodes[0].should.not.have.a.property('file'); - }); - }); - describe('#getNodeInfo', function() { - it('returns node info', function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - }}); - var nodeSetInfo = typeRegistry.getNodeInfo("test-module/test-name"); - nodeSetInfo.should.have.a.property('id',"test-module/test-name"); - nodeSetInfo.should.not.have.a.property('config'); - nodeSetInfo.should.not.have.a.property('file'); - }); - }); - describe('#getFullNodeInfo', function() { - it('returns node info', function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - - } - }}); - var nodeSetInfo = typeRegistry.getFullNodeInfo("test-module/test-name"); - nodeSetInfo.should.have.a.property('id',"test-module/test-name"); - nodeSetInfo.should.have.a.property('config'); - nodeSetInfo.should.have.a.property('file'); - }); - }); - describe('#cleanModuleList', function() { - it.skip("cleans the module list"); - }); - describe('#getNodeList', function() { - it("returns a filtered list", function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - }, - "test-name-2":{ - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-c","test-d"], - file: "def" - } - }}); - var filterCallCount = 0; - var filteredList = typeRegistry.getNodeList(function(n) { filterCallCount++; return n.name === 'test-name-2';}); - filterCallCount.should.eql(2); - filteredList.should.have.a.lengthOf(1); - filteredList[0].should.have.a.property('id',"test-module/test-name-2"); - }); - }); - - describe('#registerNodeConstructor', function() { - var TestNodeConstructor; - beforeEach(function() { - TestNodeConstructor = function TestNodeConstructor() {}; - sinon.stub(events,'emit'); - }); - afterEach(function() { - events.emit.restore(); - }); - it('registers a node constructor', function() { - typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor); - events.emit.calledOnce.should.be.true(); - events.emit.lastCall.args[0].should.eql('type-registered'); - events.emit.lastCall.args[1].should.eql('node-type'); - }) - it('throws error on duplicate node registration', function() { - typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor); - events.emit.calledOnce.should.be.true(); - events.emit.lastCall.args[0].should.eql('type-registered'); - events.emit.lastCall.args[1].should.eql('node-type'); - /*jshint immed: false */ - (function(){ - typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor); - }).should.throw("node-type already registered"); - events.emit.calledOnce.should.be.true(); - }); - }); - - describe('#getNodeIconPath', function() { - it('returns the null when getting an unknown icon', function() { - var iconPath = typeRegistry.getNodeIconPath('random-module','youwonthaveme.png'); - should.not.exist(iconPath); - }); - - it('returns a registered icon' , function() { - var testIcon = path.resolve(__dirname+'/resources/userDir/lib/icons/'); - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - },icons: [{path:testIcon,icons:['test_icon.png']}]}); - var iconPath = typeRegistry.getNodeIconPath('test-module','test_icon.png'); - iconPath.should.eql(path.resolve(testIcon+"/test_icon.png")); - }); - - it('returns null when getting an unknown module', function() { - var debugIcon = path.resolve(__dirname+'/../../../public/icons/debug.png'); - var iconPath = typeRegistry.getNodeIconPath('unknown-module', 'debug.png'); - should.not.exist(iconPath); - }); - }); - - describe('#getNodeIcons', function() { - it('returns empty icon list when no modules are registered', function() { - var iconList = typeRegistry.getNodeIcons(); - iconList.should.eql({}); - }); - - it('returns an icon list of registered node module', function() { - var testIcon = path.resolve(__dirname+'/resources/userDir/lib/icons/'); - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - },icons: [{path:testIcon,icons:['test_icon.png']}]}); - var iconList = typeRegistry.getNodeIcons(); - iconList.should.eql({"test-module":["test_icon.png"]}); - }); - }); - - describe('#getModuleResource', function() { - beforeEach(function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({ - name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - }, - resources: { - path: path.join(__dirname, "resources","examples") - } - }); - }); - it('Returns valid resource path', function() { - const result = typeRegistry.getModuleResource("test-module","one.json"); - should.exist(result); - result.should.eql(path.join(__dirname, "resources","examples","one.json")) - }); - it('Returns null for path that tries to break out', function() { - // Note - this path exists, but we don't allow .. in the resolved path to - // avoid breaking out of the resources dir - const result = typeRegistry.getModuleResource("test-module","../../index_spec.js"); - should.not.exist(result); - }); - it('Returns null for path that does not exist', function() { - const result = typeRegistry.getModuleResource("test-module","two.json"); - should.not.exist(result); - }); - }); -}); diff --git a/test/unit/@node-red/registry/lib/resources/examples/one.json b/test/unit/@node-red/registry/lib/resources/examples/one.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js b/test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js deleted file mode 100644 index 41e2a0059..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function DuffNode(n) {} - RED.nodes.registerType("duff-node",DuffNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html b/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html deleted file mode 100644 index b637ede21..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js b/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js deleted file mode 100644 index e81214169..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function DuplicateTestNode(n) {} - RED.nodes.registerType("test-node-1",DuplicateTestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html b/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html deleted file mode 100644 index 5359644e5..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js b/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js deleted file mode 100644 index 55747c0b3..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js +++ /dev/null @@ -1,7 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode1(n) {} - RED.nodes.registerType("test-node-multiple-1a",TestNode1); - function TestNode2(n) {} - RED.nodes.registerType("test-node-multiple-1b",TestNode2); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html deleted file mode 100644 index abc823e8f..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js deleted file mode 100644 index cd3148a58..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("nested-node-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png deleted file mode 100644 index e38f3914600901b736f5fa18786ee11be6d41c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6icy_X9x!n)NrJ90QsB+9+AZi z3~X;em{G3O!W1YdS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|Bb%p- zV~9uR+N-+$hZ97Oeyp#Y$RU!&XX#m>@|$agvYL9Jz$O6}72y^gZVi>ZMeaH^t*Q|! zLhVk<0xdko8zYVo#8M-LBBd8b>1kw|i?FO)bWqsy_rwQg27N4y zeX6pqO$_Cex^^A7_NsS@+=bOASA^wN0&GKN(hlAguRU&q- jTBY?`*L>xN+|S&+7B*edvmQhOgN?z{)z4*}Q$iB}+9!`i diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html deleted file mode 100644 index ac9235d26..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js deleted file mode 100644 index 8af249b14..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("should-not-load-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html deleted file mode 100644 index eb7c8a3f9..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js deleted file mode 100644 index 623e299b2..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("should-not-load-2",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html deleted file mode 100644 index 4212fd589..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js deleted file mode 100644 index 5856adada..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("should-not-load-3",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html b/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html deleted file mode 100644 index 97dbf1710..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js b/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js deleted file mode 100644 index bfa3b65bd..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("test-node-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html b/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html deleted file mode 100644 index 66b65909e..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js b/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js deleted file mode 100644 index 1bf2fa6c9..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js +++ /dev/null @@ -1,9 +0,0 @@ -// A test node that exports a function which returns a resolving promise - -module.exports = function(RED) { - return new Promise(function(resolve,reject) { - function TestNode(n) {} - RED.nodes.registerType("test-node-2",TestNode); - resolve(); - }); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html b/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html deleted file mode 100644 index 9a0f6f7ea..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js b/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js deleted file mode 100644 index b9ee6a624..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js +++ /dev/null @@ -1,7 +0,0 @@ -// A test node that exports a function which returns a rejecting promise - -module.exports = function(RED) { - return new Promise(function(resolve,reject) { - reject("fail"); - }); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html b/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html deleted file mode 100644 index 9a0f6f7ea..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js b/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js deleted file mode 100644 index c31255852..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("fail to require"); diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt deleted file mode 100644 index 0ce8dcaed..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt +++ /dev/null @@ -1 +0,0 @@ -This file exists just to ensure the parent directory is in the repository. diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html deleted file mode 100644 index 17483f7ca..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js deleted file mode 100644 index c79c2ceb6..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("test-node-mod-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html deleted file mode 100644 index a1f1b6c79..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js deleted file mode 100644 index d359fb3f2..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js +++ /dev/null @@ -1,4 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - throw new Error("fail to load"); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png deleted file mode 100644 index e38f3914600901b736f5fa18786ee11be6d41c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6icy_X9x!n)NrJ90QsB+9+AZi z3~X;em{G3O!W1YdS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|Bb%p- zV~9uR+N-+$hZ97Oeyp#Y$RU!&XX#m>@|$agvYL9Jz$O6}72y^gZVi>ZMeaH^t*Q|! zLhVk<0xdko8zYVo#8M-LBBd8b>1kw|i?FO)bWqsy_rwQg27N4y zeX6pqO$_Cex^^A7_NsS@+=bOASA^wN0&GKN(hlAguRU&q- jTBY?`*L>xN+|S&+7B*edvmQhOgN?z{)z4*}Q$iB}+9!`i diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt deleted file mode 100644 index 59a29af14..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt +++ /dev/null @@ -1,3 +0,0 @@ -This file exists just to ensure the 'icons' directory is in the repository. -TODO: a future test needs to ensure the right icon files are loaded - this - directory can be used for that diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json deleted file mode 100644 index 8eba5b04b..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name" : "TestNodeModule", - "version" : "0.0.1", - "description" : "A test node module", - "node-red" : { - "nodes": { - "TestNodeMod1": "TestNodeModule.js", - "TestNodeMod2": "TestNodeModule2.js" - } - } -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html deleted file mode 100644 index 17483f7ca..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js deleted file mode 100644 index c79c2ceb6..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("test-node-mod-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html deleted file mode 100644 index a1f1b6c79..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js deleted file mode 100644 index d359fb3f2..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js +++ /dev/null @@ -1,4 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - throw new Error("fail to load"); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt deleted file mode 100644 index 59a29af14..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt +++ /dev/null @@ -1,3 +0,0 @@ -This file exists just to ensure the 'icons' directory is in the repository. -TODO: a future test needs to ensure the right icon files are loaded - this - directory can be used for that diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json deleted file mode 100644 index 4f9e46518..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name" : "VersionMismatchModule", - "version" : "0.0.1", - "description" : "A test node module", - "node-red" : { - "version": "100.0.0", - "nodes": { - "VersionMismatchMod1": "TestNodeModule.js", - "VersionMismatchMod2": "TestNodeModule2.js" - } - } -} diff --git a/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/file.txt b/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/file.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/test_icon.png b/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/test_icon.png deleted file mode 100644 index 4b6b7b5e9b10ec790348eb59f73bdac596e2f3b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1$P6UK?yS)OQjEnx?oJHr&dIz4a@YcVLR^9L z|NsA&-kg6IB%S2#?!wT)D(eB{a29w(76Vni0bxeDQVUa{AbW|YuPggKZdPtVo=;b% zu>pm|JY5_^IIbrr*#Br`l>NUU`M`mYFm{Gnm$g4<{K@tLs$lSR^>bP0l+XkK5O^!s diff --git a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html b/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html deleted file mode 100644 index f38a3a24d..000000000 --- a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js b/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js deleted file mode 100644 index c31255852..000000000 --- a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("fail to require"); diff --git a/test/unit/@node-red/registry/lib/subflow_spec.js b/test/unit/@node-red/registry/lib/subflow_spec.js deleted file mode 100644 index 2aa7db203..000000000 --- a/test/unit/@node-red/registry/lib/subflow_spec.js +++ /dev/null @@ -1,3 +0,0 @@ -describe("red/nodes/registry/subflow",function() { - it.skip("NEEDS TESTS"); -}); diff --git a/test/unit/@node-red/registry/lib/util_spec.js b/test/unit/@node-red/registry/lib/util_spec.js deleted file mode 100644 index 7e94d4fe5..000000000 --- a/test/unit/@node-red/registry/lib/util_spec.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -const should = require("should"); -const sinon = require("sinon"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const registryUtil = NR_TEST_UTILS.require("@node-red/registry/lib/util"); - -// Get the internal runtime api -const runtime = NR_TEST_UTILS.require("@node-red/runtime")._; - -const i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("red/nodes/registry/util",function() { - describe("createNodeApi", function() { - let i18n_; - let registerType; - let registerSubflow; - - before(function() { - i18n_ = sinon.stub(i18n,"_").callsFake(function() { - return Array.prototype.slice.call(arguments,0); - }) - registerType = sinon.stub(runtime.nodes,"registerType"); - registerSubflow = sinon.stub(runtime.nodes,"registerSubflow"); - }); - after(function() { - i18n_.restore(); - registerType.restore(); - registerSubflow.restore(); - }) - - it("builds node-specific view of runtime api", function() { - registryUtil.init(runtime); - var result = registryUtil.createNodeApi({id: "my-node", namespace: "my-namespace"}) - // Need a better strategy here. - // For now, validate the node-custom functions - - var message = result._("message"); - // This should prepend the node's namespace to the message - message.should.eql([ 'my-namespace:message' ]); - - var nodeConstructor = () => {}; - var nodeOpts = {}; - result.nodes.registerType("type",nodeConstructor, nodeOpts); - registerType.called.should.be.true(); - registerType.lastCall.args[0].should.eql("my-node") - registerType.lastCall.args[1].should.eql("type") - registerType.lastCall.args[2].should.eql(nodeConstructor) - registerType.lastCall.args[3].should.eql(nodeOpts) - - var subflowDef = {}; - result.nodes.registerSubflow(subflowDef); - registerSubflow.called.should.be.true(); - registerSubflow.lastCall.args[0].should.eql("my-node") - registerSubflow.lastCall.args[1].should.eql(subflowDef) - - }); - }); - describe("checkModuleAllowed", function() { - function checkList(module, version, allowList, denyList) { - return registryUtil.checkModuleAllowed( - module, - version, - registryUtil.parseModuleList(allowList), - registryUtil.parseModuleList(denyList) - ) - } - - it("allows module with no allow/deny list provided", function() { - checkList("abc","1.2.3",[],[]).should.be.true(); - }) - it("defaults allow to * when only deny list is provided", function() { - checkList("abc","1.2.3",["*"],["def"]).should.be.true(); - checkList("def","1.2.3",["*"],["def"]).should.be.false(); - }) - it("uses most specific matching rule", function() { - checkList("abc","1.2.3",["ab*"],["a*"]).should.be.true(); - checkList("def","1.2.3",["d*"],["de*"]).should.be.false(); - }) - it("checks version string using semver rules", function() { - // Deny - checkList("abc","1.2.3",["abc@1.2.2"],["*"]).should.be.false(); - checkList("abc","1.2.3",["abc@1.2.4"],["*"]).should.be.false(); - checkList("abc","1.2.3",["abc@>1.2.3"],["*"]).should.be.false(); - checkList("abc","1.2.3",["abc@>=1.2.3"],["abc"]).should.be.false(); - - - checkList("node-red-contrib-foo","1.2.3",["*"],["*contrib*"]).should.be.false(); - - - // Allow - checkList("abc","1.2.3",["abc@1.2.3"],["*"]).should.be.true(); - checkList("abc","1.2.3",["abc@<1.2.4"],["*"]).should.be.true(); - checkList("abc","1.2.3",["abc"],["abc@>1.2.3"]).should.be.true(); - checkList("abc","1.2.3",["abc"],["abc@<1.2.3||>1.2.3"]).should.be.true(); - checkList("node-red-contrib-foo","1.2.3",["*contrib*"],["*"]).should.be.true(); - }) - - }) -}); diff --git a/test/unit/@node-red/runtime/lib/api/comms_spec.js b/test/unit/@node-red/runtime/lib/api/comms_spec.js deleted file mode 100644 index 0097f5ce8..000000000 --- a/test/unit/@node-red/runtime/lib/api/comms_spec.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var comms = NR_TEST_UTILS.require("@node-red/runtime/lib/api/comms"); -var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); - -describe("runtime-api/comms", function() { - describe("listens for events", function() { - var messages = []; - var clientConnection = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var eventHandlers = {}; - before(function(done) { - sinon.stub(events,"removeListener").callsFake(function() {}) - sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler }) - comms.init({ - log: { - trace: function(){} - } - }) - comms.addConnection({client: clientConnection}).then(done); - }) - after(function(done) { - comms.removeConnection({client: clientConnection}).then(done); - events.removeListener.restore(); - events.on.restore(); - }) - afterEach(function() { - messages = []; - }) - - it('runtime events',function(){ - eventHandlers.should.have.property('runtime-event'); - eventHandlers['runtime-event']({ - id: "my-event", - payload: "my-payload" - }) - messages.should.have.length(1); - messages[0].should.have.property("topic","notification/my-event"); - messages[0].should.have.property("data","my-payload") - }) - it('status events',function(){ - eventHandlers.should.have.property('node-status'); - eventHandlers['node-status']({ - id: "my-event", - status: {text:"my-status",badProperty:"should be filtered"} - }) - messages.should.have.length(1); - messages[0].should.have.property("topic","status/my-event"); - messages[0].should.have.property("data"); - messages[0].data.should.have.property("text","my-status"); - messages[0].data.should.not.have.property("badProperty"); - - }) - it('comms events',function(){ - eventHandlers.should.have.property('runtime-event'); - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(1); - messages[0].should.have.property("topic","my-topic"); - messages[0].should.have.property("data","my-payload") - }) - }); - describe("manages connections", function() { - var eventHandlers = {}; - var messages = []; - var clientConnection1 = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var clientConnection2 = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - before(function() { - sinon.stub(events,"removeListener").callsFake(function() {}) - sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler }) - comms.init({ - log: { - trace: function(){} - } - }) - }) - after(function() { - events.removeListener.restore(); - events.on.restore(); - }) - afterEach(function(done) { - comms.removeConnection({client: clientConnection1}).then(function() { - comms.removeConnection({client: clientConnection2}).then(done); - }); - messages = []; - }) - it('adds new connections',function(done){ - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection1}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(1); - comms.addConnection({client: clientConnection2}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(3); - done(); - }).catch(done); - }); - }); - it('removes connections',function(done){ - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection1}).then(function() { - comms.addConnection({client: clientConnection2}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(2); - comms.removeConnection({client: clientConnection1}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(3); - done(); - }); - }).catch(done); - }); - }) - }) - - describe("subscriptions", function() { - var messages = []; - var clientConnection = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var clientConnection2 = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var eventHandlers = {}; - before(function() { - sinon.stub(events,"removeListener").callsFake(function() {}) - sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler }) - comms.init({ - log: { - trace: function(){} - } - }) - }) - after(function() { - events.removeListener.restore(); - events.on.restore(); - }) - afterEach(function(done) { - messages = []; - comms.removeConnection({client: clientConnection}).then(done); - }) - - it('subscribe triggers retained messages',function(done){ - eventHandlers['comms']({ - topic: "my-event", - data: "my-payload", - retain: true - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","my-event"); - messages[0].should.have.property("data","my-payload"); - done(); - }); - }).catch(done); - }) - it('retains non-blank status message',function(done){ - eventHandlers['node-status']({ - id: "node1234", - status: {text:"hello"} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","status/node1234"); - messages[0].should.have.property("data",{text:"hello", fill: undefined, shape: undefined}); - done(); - }); - }).catch(done); - }) - it('does not retain blank status message',function(done){ - eventHandlers['node-status']({ - id: "node1234", - status: {} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - messages.should.have.length(0); - done(); - }); - }).catch(done); - }) - it('does not send blank status if first status',function(done){ - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - eventHandlers['node-status']({ - id: "node5678", - status: {} - }) - messages.should.have.length(0); - done() - }) - }).catch(done); - }); - it('sends blank status if replacing retained',function(done){ - eventHandlers['node-status']({ - id: "node5678", - status: {text:"hello"} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - messages.should.have.length(1); - eventHandlers['node-status']({ - id: "node5678", - status: {} - }) - messages.should.have.length(2); - done() - }) - }).catch(done); - }); - - it('does not retain initial status blank message',function(done){ - eventHandlers['node-status']({ - id: "my-event", - status: {} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","my-event"); - messages[0].should.have.property("data","my-payload"); - done(); - }); - }).catch(done); - }) - - it('retained messages get cleared',function(done) { - eventHandlers['comms']({ - topic: "my-event", - data: "my-payload", - retain: true - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","my-event"); - messages[0].should.have.property("data","my-payload"); - // Now we have a retained message, clear it - eventHandlers['comms']({ - topic: "my-event", - data: "my-payload-cleared" - }); - messages.should.have.length(2); - messages[1].should.have.property("topic","my-event"); - messages[1].should.have.property("data","my-payload-cleared"); - // Now add a second client and subscribe - no message should arrive - return comms.addConnection({client: clientConnection2}).then(function() { - return comms.subscribe({client: clientConnection2, topic: "my-event"}).then(function() { - messages.should.have.length(2); - done(); - }); - }); - }); - }).catch(done); - }); - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/api/context_spec.js b/test/unit/@node-red/runtime/lib/api/context_spec.js deleted file mode 100644 index bf23e9cc6..000000000 --- a/test/unit/@node-red/runtime/lib/api/context_spec.js +++ /dev/null @@ -1,353 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var context = NR_TEST_UTILS.require("@node-red/runtime/lib/api/context"); - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc";} -}); - -var mockContext = function(contents) { - return { - get: function(key,store,callback) { - if (contents.hasOwnProperty(store) && contents[store].hasOwnProperty(key)) { - callback(null,contents[store][key]); - } else { - callback(null,undefined); - } - }, - set: function (key, value, store, callback) { - if (contents.hasOwnProperty(store)) { - if (!value) { - delete contents[store][key]; - callback(null); - } - } else { - callback("err store"); - } - }, - keys: function (store, callback) { - if (contents.hasOwnProperty(store)) { - callback(null, Object.keys(contents[store])); - } else { - callback("err store"); - } - } - }; -}; - -describe("runtime-api/context", function() { - var globalContext, flowContext, nodeContext, contexts; - - beforeEach(function() { - globalContext = { default: { abc: 111 }, file: { abc: 222 } }; - flowContext = { default: { abc: 333 }, file: { abc: 444 } }; - nodeContext = { default: { abc: 555 }, file: { abc: 666 } }; - contexts = { - global: mockContext(globalContext), - flow1: mockContext(flowContext) - }; - context.init({ - nodes: { - listContextStores: function() { - return { default: 'default', stores: [ 'default', 'file' ] }; - }, - getContext: function(id) { - return contexts[id]; - }, - getNode: function(id) { - if (id === 'known') { - return { - context: function() { return mockContext(nodeContext); } - }; - } else { - return null; - } - } - }, - settings: { - functionGlobalContext: { - fgc:1234 - } - }, - log: mockLog() - }); - }); - - describe("getValue", function() { - it('gets global value of default store', function() { - return context.getValue({ - scope: 'global', - id: undefined, - store: undefined, // use default - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','111'); - result.should.have.property('format','number'); - }); - }); - - it('gets global value of specified store', function() { - return context.getValue({ - scope: 'global', - id: undefined, - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','222'); - result.should.have.property('format','number'); - }); - }); - - it('gets flow value of default store', function() { - return context.getValue({ - scope: 'flow', - id: 'flow1', - store: undefined, // use default - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','333'); - result.should.have.property('format','number'); - }); - }); - - it('gets flow value of specified store', function() { - return context.getValue({ - scope: 'flow', - id: 'flow1', - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','444'); - result.should.have.property('format','number'); - }); - }); - - it('gets node value of default store', function() { - return context.getValue({ - scope: 'node', - id: 'known', - store: undefined, // use default - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','555'); - result.should.have.property('format','number'); - }); - }); - - it('gets node value of specified store', function() { - return context.getValue({ - scope: 'node', - id: 'known', - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','666'); - result.should.have.property('format','number'); - }); - }); - - it('404s for unknown store', function(done) { - context.getValue({ - scope: 'global', - id: undefined, - store: 'unknown', - key: 'abc' - }).then(function(result) { - done("getValue for unknown store should not resolve"); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }); - }); - - it('gets all global value properties', function() { - return context.getValue({ - scope: 'global', - id: undefined, - store: undefined, // use default - key: undefined, // - }).then(function(result) { - result.should.eql({ - default: { abc: { msg: '111', format: 'number' } }, - file: { abc: { msg: '222', format: 'number' } } - }); - }); - }); - - it('gets all flow value properties', function() { - return context.getValue({ - scope: 'flow', - id: 'flow1', - store: undefined, // use default - key: undefined, // - }).then(function(result) { - result.should.eql({ - default: { abc: { msg: '333', format: 'number' } }, - file: { abc: { msg: '444', format: 'number' } } - }); - }); - }); - - it('gets all node value properties', function() { - return context.getValue({ - scope: 'node', - id: 'known', - store: undefined, // use default - key: undefined, // - }).then(function(result) { - result.should.eql({ - default: { abc: { msg: '555', format: 'number' } }, - file: { abc: { msg: '666', format: 'number' } } - }); - }); - }); - - it('gets empty object when specified context doesn\'t exist', function() { - return context.getValue({ - scope: 'node', - id: 'non-existent', - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.be.an.Object(); - result.should.be.empty(); - }); - }); - }); - - describe("delete", function () { - it('deletes global value of default store', function () { - return context.delete({ - scope: 'global', - id: undefined, - store: undefined, // use default - key: 'abc' - }).then(function () { - globalContext.should.eql({ - default: {}, file: { abc: 222 } - }); - }); - }); - - it('deletes global value of specified store', function () { - return context.delete({ - scope: 'global', - id: undefined, - store: 'file', - key: 'abc' - }).then(function () { - globalContext.should.eql({ - default: { abc: 111 }, file: {} - }); - }); - }); - - it('deletes flow value of default store', function () { - return context.delete({ - scope: 'flow', - id: 'flow1', - store: undefined, // use default - key: 'abc' - }).then(function () { - flowContext.should.eql({ - default: {}, file: { abc: 444 } - }); - }); - }); - - it('deletes flow value of specified store', function () { - return context.delete({ - scope: 'flow', - id: 'flow1', - store: 'file', - key: 'abc' - }).then(function () { - flowContext.should.eql({ - default: { abc: 333 }, file: {} - }); - }); - }); - - it('deletes node value of default store', function () { - return context.delete({ - scope: 'node', - id: 'known', - store: undefined, // use default - key: 'abc' - }).then(function () { - nodeContext.should.eql({ - default: {}, file: { abc: 666 } - }); - }); - }); - - it('deletes node value of specified store', function () { - return context.delete({ - scope: 'node', - id: 'known', - store: 'file', - key: 'abc' - }).then(function () { - nodeContext.should.eql({ - default: { abc: 555 }, file: {} - }); - }); - }); - - it('does nothing when specified context doesn\'t exist', function() { - return context.delete({ - scope: 'node', - id: 'non-existent', - store: 'file', - key: 'abc' - }).then(function(result) { - should.not.exist(result); - nodeContext.should.eql({ - default: { abc: 555 }, file: { abc: 666 } - }); - }); - }); - - it('404s for unknown store', function (done) { - context.delete({ - scope: 'global', - id: undefined, - store: 'unknown', - key: 'abc' - }).then(function () { - done("delete for unknown store should not resolve"); - }).catch(function (err) { - err.should.have.property('code', 'not_found'); - err.should.have.property('status', 404); - done(); - }); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/api/flows_spec.js b/test/unit/@node-red/runtime/lib/api/flows_spec.js deleted file mode 100644 index 9062ef52f..000000000 --- a/test/unit/@node-red/runtime/lib/api/flows_spec.js +++ /dev/null @@ -1,430 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/api/flows") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/flows", function() { - describe("getFlows", function() { - it("returns the current flow configuration", function(done) { - flows.init({ - log: mockLog(), - flows: { - getFlows: function() { return [1,2,3] } - } - }); - flows.getFlows({}).then(function(result) { - result.should.eql([1,2,3]); - done(); - }).catch(done); - }); - }); - - describe("setFlows", function() { - var setFlows; - var loadFlows; - var reloadError = false; - beforeEach(function() { - setFlows = sinon.spy(function(flows,credentials,type) { - if (flows[0] === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve("newRev"); - }); - loadFlows = sinon.spy(function() { - if (!reloadError) { - return Promise.resolve("newLoadRev"); - } else { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - }) - flows.init({ - log: mockLog(), - flows: { - getFlows: function() { return {rev:"currentRev",flows:[]} }, - setFlows: setFlows, - loadFlows: loadFlows - } - }) - - }) - it("defaults to full deploy", function(done) { - flows.setFlows({ - flows: {flows:[4,5,6]} - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[2].should.eql("full"); - done(); - }).catch(done); - }); - it("includes credentials when part of the request", function(done) { - flows.setFlows({ - flows: {flows:[4,5,6], credentials: {$:"creds"}}, - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[1].should.eql({$:"creds"}); - setFlows.lastCall.args[2].should.eql("full"); - done(); - }).catch(done); - }); - it("passes through other deploy types", function(done) { - flows.setFlows({ - deploymentType: "nodes", - flows: {flows:[4,5,6]} - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[2].should.eql("nodes"); - done(); - }).catch(done); - }); - it("triggers a flow reload", function(done) { - flows.setFlows({ - deploymentType: "reload" - }).then(function(result) { - result.should.eql({rev:"newLoadRev"}); - setFlows.called.should.be.false(); - loadFlows.called.should.be.true(); - done(); - }).catch(done); - }); - it("allows update when revision matches", function(done) { - flows.setFlows({ - deploymentType: "nodes", - flows: {flows:[4,5,6],rev:"currentRev"} - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[2].should.eql("nodes"); - done(); - }).catch(done); - }); - it("rejects update when revision does not match", function(done) { - flows.setFlows({ - deploymentType: "nodes", - flows: {flows:[4,5,6],rev:"notTheCurrentRev"} - }).then(function(result) { - done(new Error("Did not reject rev mismatch")); - }).catch(function(err) { - err.should.have.property('code','version_mismatch'); - err.should.have.property('status',409); - done(); - }).catch(done); - }); - it("rejects when reload fails",function(done) { - reloadError = true; - flows.setFlows({ - deploymentType: "reload" - }).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - done(); - }).catch(done); - }); - it("rejects when update fails",function(done) { - flows.setFlows({ - deploymentType: "full", - flows: {flows:["error",5,6]} - }).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - done(); - }).catch(done); - }); - }); - - describe("addFlow", function() { - var addFlow; - beforeEach(function() { - addFlow = sinon.spy(function(flow) { - if (flow === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve("newId"); - }); - flows.init({ - log: mockLog(), - flows: { - addFlow: addFlow - } - }); - }) - it("adds a flow", function(done) { - flows.addFlow({flow:{a:"123"}}).then(function(id) { - addFlow.called.should.be.true(); - addFlow.lastCall.args[0].should.eql({a:"123"}); - id.should.eql("newId"); - done() - }).catch(done); - }); - it("rejects when add fails", function(done) { - flows.addFlow({flow:"error"}).then(function(id) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - done(); - }).catch(done); - }); - }); - describe("getFlow", function() { - var getFlow; - beforeEach(function() { - getFlow = sinon.spy(function(flow) { - if (flow === "unknown") { - return null; - } - return [1,2,3]; - }); - flows.init({ - log: mockLog(), - flows: { - getFlow: getFlow - } - }); - }) - it("gets a flow", function(done) { - flows.getFlow({id:"123"}).then(function(flow) { - flow.should.eql([1,2,3]); - done() - }).catch(done); - }); - it("rejects when flow not found", function(done) { - flows.getFlow({id:"unknown"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - - describe("updateFlow", function() { - var updateFlow; - beforeEach(function() { - updateFlow = sinon.spy(function(id,flow) { - if (id === "unknown") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = 404; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (id === "error") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve(); - }); - flows.init({ - log: mockLog(), - flows: { - updateFlow: updateFlow - } - }); - }) - it("updates a flow", function(done) { - flows.updateFlow({id:"123",flow:[1,2,3]}).then(function(id) { - id.should.eql("123"); - updateFlow.called.should.be.true(); - updateFlow.lastCall.args[0].should.eql("123"); - updateFlow.lastCall.args[1].should.eql([1,2,3]); - done() - }).catch(done); - }); - it("rejects when flow not found", function(done) { - flows.updateFlow({id:"unknown"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - it("rejects when update fails", function(done) { - flows.updateFlow({id:"error"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - - describe("deleteFlow", function() { - var removeFlow; - beforeEach(function() { - removeFlow = sinon.spy(function(flow) { - if (flow === "unknown") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = 404; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (flow === "error") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve(); - }); - flows.init({ - log: mockLog(), - flows: { - removeFlow: removeFlow - } - }); - }) - it("deletes a flow", function(done) { - flows.deleteFlow({id:"123"}).then(function() { - removeFlow.called.should.be.true(); - removeFlow.lastCall.args[0].should.eql("123"); - done() - }).catch(done); - }); - it("rejects when flow not found", function(done) { - flows.deleteFlow({id:"unknown"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - it("rejects when delete fails", function(done) { - flows.deleteFlow({id:"error"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getNodeCredentials", function() { - beforeEach(function() { - flows.init({ - log: mockLog(), - nodes: { - getCredentials: function(id) { - if (id === "unknown") { - return undefined; - } else if (id === "known") { - return { - username: "abc", - password: "123" - } - } else if (id === "known2") { - return { - username: "abc", - password: "" - } - } else { - return {}; - } - }, - getCredentialDefinition: function(type) { - if (type === "node") { - return { - username: {type:"text"}, - password: {type:"password"} - } - } else { - return null; - } - } - } - }); - }) - it("returns an empty object for an unknown node", function(done) { - flows.getNodeCredentials({id:"unknown", type:"node"}).then(function(result) { - result.should.eql({}); - done(); - }).catch(done); - }); - it("gets the filtered credentials for a known node with password", function(done) { - flows.getNodeCredentials({id:"known", type:"node"}).then(function(result) { - result.should.eql({ - username: "abc", - has_password: true - }); - done(); - }).catch(done); - }); - it("gets the filtered credentials for a known node without password", function(done) { - flows.getNodeCredentials({id:"known2", type:"node"}).then(function(result) { - result.should.eql({ - username: "abc", - has_password: false - }); - done(); - }).catch(done); - }); - it("gets the empty credentials for a known node without a registered definition", function(done) { - flows.getNodeCredentials({id:"known2", type:"unknown-type"}).then(function(result) { - result.should.eql({}); - done(); - }).catch(done); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/api/index_spec.js b/test/unit/@node-red/runtime/lib/api/index_spec.js deleted file mode 100644 index ecedf22ef..000000000 --- a/test/unit/@node-red/runtime/lib/api/index_spec.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var index = NR_TEST_UTILS.require("@node-red/runtime/lib/api/index"); - - -describe("runtime-api/index", function() { - before(function() { - ["comms","flows","nodes","settings","library","projects"].forEach(n => { - sinon.stub(NR_TEST_UTILS.require(`@node-red/runtime/lib/api/${n}`),"init").callsFake(()=>{}); - }) - }); - after(function() { - ["comms","flows","nodes","settings","library","projects"].forEach(n => { - NR_TEST_UTILS.require(`@node-red/runtime/lib/api/${n}`).init.restore() - }) - }) - it('isStarted', function(done) { - index.init({ - isStarted: ()=>true - }); - index.isStarted({}).then(function(started) { - started.should.be.true(); - done(); - }).catch(done); - }) - - it('isStarted', function(done) { - index.init({ - version: ()=>"1.2.3.4" - }); - index.version({}).then(function(version) { - version.should.eql("1.2.3.4"); - done(); - }).catch(done); - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/api/library_spec.js b/test/unit/@node-red/runtime/lib/api/library_spec.js deleted file mode 100644 index 3fa5291d2..000000000 --- a/test/unit/@node-red/runtime/lib/api/library_spec.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var library = NR_TEST_UTILS.require("@node-red/runtime/lib/api/library") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - -describe("runtime-api/library", function() { - describe("getEntry", function() { - before(function() { - library.init({ - log: mockLog, - library: { - getEntry: function(library, type,path) { - if (type === "known") { - return Promise.resolve("known"); - } else if (type === "forbidden") { - var err = new Error("forbidden"); - err.code = "forbidden"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "not_found") { - var err = new Error("forbidden"); - err.code = "not_found"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "error") { - var err = new Error("error"); - err.code = "unknown_error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "blank") { - return Promise.reject(); - } - } - } - }) - }) - it("returns a known entry", function(done) { - library.getEntry({library: "local",type: "known", path: "/abc"}).then(function(result) { - result.should.eql("known") - done(); - }).catch(done) - }) - it("rejects a forbidden entry", function(done) { - library.getEntry({library: "local",type: "forbidden", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","forbidden"); - err.should.have.property("status",403); - done(); - }).catch(done) - }) - it("rejects an unknown entry", function(done) { - library.getEntry({library: "local",type: "not_found", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","not_found"); - err.should.have.property("status",404); - done(); - }).catch(done) - }) - it("rejects a blank (unknown) entry", function(done) { - library.getEntry({library: "local",type: "blank", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","not_found"); - err.should.have.property("status",404); - done(); - }).catch(done) - }) - it("rejects unexpected error", function(done) { - library.getEntry({library: "local",type: "error", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("status",400); - done(); - }).catch(done) - }) - }) - describe("saveEntry", function() { - var opts; - before(function() { - library.init({ - log: mockLog, - library: { - saveEntry: function(library,type,path,meta,body) { - opts = {type,path,meta,body}; - if (type === "known") { - return Promise.resolve(); - } else if (type === "forbidden") { - var err = new Error("forbidden"); - err.code = "forbidden"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "not_found") { - var err = new Error("forbidden"); - err.code = "not_found"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - } - } - }) - }) - - it("saves an entry", function(done) { - library.saveEntry({library: "local",type: "known", path: "/abc", meta: {a:1}, body:"123"}).then(function() { - opts.should.have.property("type","known"); - opts.should.have.property("path","/abc"); - opts.should.have.property("meta",{a:1}); - opts.should.have.property("body","123"); - done(); - }).catch(done) - }) - it("rejects a forbidden entry", function(done) { - library.saveEntry({library: "local",type: "forbidden", path: "/abc", meta: {a:1}, body:"123"}).then(function() { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","forbidden"); - err.should.have.property("status",403); - done(); - }).catch(done) - }) - it("rejects an unknown entry", function(done) { - library.saveEntry({library: "local",type: "not_found", path: "/abc", meta: {a:1}, body:"123"}).then(function() { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("status",400); - done(); - }).catch(done) - }) - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/api/nodes_spec.js b/test/unit/@node-red/runtime/lib/api/nodes_spec.js deleted file mode 100644 index 60c6d9edd..000000000 --- a/test/unit/@node-red/runtime/lib/api/nodes_spec.js +++ /dev/null @@ -1,1005 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var nodes = NR_TEST_UTILS.require("@node-red/runtime/lib/api/nodes") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/nodes", function() { - describe("getNodeInfo", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeInfo: function(id) { - if (id === "known") { - return {id:"known"}; - } else { - return null; - } - } - } - }); - }) - it("returns node info", function(done) { - nodes.getNodeInfo({id:"known"}).then(function(result) { - result.should.eql({id:"known"}); - done(); - }).catch(done); - }); - it("returns 404 if node not known", function(done) { - nodes.getNodeInfo({id:"unknown"}).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - describe("getNodeList", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeList: function() { - return [1,2,3]; - } - } - }); - }) - it("returns node list", function(done) { - nodes.getNodeList({}).then(function(result) { - result.should.eql([1,2,3]); - done(); - }).catch(done); - }); - }); - - describe("getNodeConfig", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeConfig: function(id,lang) { - if (id === "known") { - return id+lang; - } else { - return null; - } - } - } - }); - }) - it("returns node config", function(done) { - nodes.getNodeConfig({id:"known",lang:'lang'}).then(function(result) { - result.should.eql("knownlang"); - done(); - }).catch(done); - }); - it("returns 404 if node not known", function(done) { - nodes.getNodeConfig({id:"unknown",lang:'lang'}).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - - describe("getNodeConfigs", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeConfigs: function(lang) { - return lang; - } - } - }); - }) - it("returns all node configs", function(done) { - nodes.getNodeConfigs({lang:'lang'}).then(function(result) { - result.should.eql("lang"); - done(); - }).catch(done); - }); - }); - - describe("getModuleInfo", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getModuleInfo: function(id) { - if (id === "known") { - return {module:"known"}; - } else { - return null; - } - } - } - }); - }) - it("returns node info", function(done) { - nodes.getModuleInfo({module:"known"}).then(function(result) { - result.should.eql({module:"known"}); - done(); - }).catch(done); - }); - it("returns 404 if node not known", function(done) { - nodes.getModuleInfo({module:"unknown"}).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - - describe.skip("addModule", function() {}); - describe.skip("removeModule", function() {}); - describe.skip("setModuleState", function() {}); - describe.skip("setNodeSetState", function() {}); - - describe.skip("getModuleCatalogs", function() {}); - describe.skip("getModuleCatalog", function() {}); - - describe.skip("getIconList", function() {}); - describe.skip("getIcon", function() {}); - - -}); - -/* -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require('body-parser'); -var sinon = require('sinon'); - -var nodes = require("../../../../red/api/admin/nodes"); -var apiUtil = require("../../../../red/api/util"); - -describe("api/admin/nodes", function() { - - var app; - function initNodes(runtime) { - runtime.log = { - audit:function(e){},//console.log(e)}, - _:function(){}, - info: function(){}, - warn: function(){} - } - runtime.events = { - emit: function(){} - } - nodes.init(runtime); - - } - - before(function() { - app = express(); - app.use(bodyParser.json()); - app.get("/nodes",nodes.getAll); - app.post("/nodes",nodes.post); - app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule); - app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet); - app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule); - app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet); - app.get("/getIcons",nodes.getIcons); - app.delete("/nodes/:id",nodes.delete); - sinon.stub(apiUtil,"determineLangFromHeaders").callsFake(function() { - return "en-US"; - }); - }); - after(function() { - apiUtil.determineLangFromHeaders.restore(); - }) - - describe('get nodes', function() { - it('returns node list', function(done) { - initNodes({ - nodes:{ - getNodeList: function() { - return [1,2,3]; - } - } - }); - request(app) - .get('/nodes') - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.be.an.Array(); - res.body.should.have.lengthOf(3); - done(); - }); - }); - - it('returns node configs', function(done) { - initNodes({ - nodes:{ - getNodeConfigs: function() { - return ""; - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns node module info', function(done) { - initNodes({ - nodes:{ - getModuleInfo: function(id) { - return {"node-red":{name:"node-red"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-red') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - done(); - }); - }); - - it('returns 404 for unknown module', function(done) { - initNodes({ - nodes:{ - getModuleInfo: function(id) { - return {"node-red":{name:"node-red"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-blue') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns individual node info', function(done) { - initNodes({ - nodes:{ - getNodeInfo: function(id) { - return {"node-red/123":{id:"node-red/123"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","node-red/123"); - done(); - }); - }); - - it('returns individual node configs', function(done) { - initNodes({ - nodes:{ - getNodeConfig: function(id) { - return {"node-red/123":""}[id]; - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 404 for unknown node', function(done) { - initNodes({ - nodes:{ - getNodeInfo: function(id) { - return {"node-red/123":{id:"node-red/123"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-red/456') - .set('Accept', 'application/json') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - }); - - describe('install', function() { - - it('returns 400 if settings are unavailable', function(done) { - initNodes({ - settings:{available:function(){return false}} - }); - request(app) - .post('/nodes') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 400 if request is invalid', function(done) { - initNodes({ - settings:{available:function(){return true}} - }); - request(app) - .post('/nodes') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - describe('by module', function() { - it('installs the module and returns module info', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null; }, - installModule: function() { - return Promise.resolve({ - name:"foo", - nodes:[{id:"123"}] - }); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","foo"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("id","123"); - done(); - }); - }); - - it('fails the install if already installed', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return {nodes:{id:"123"}}; }, - installModule: function() { - return Promise.resolve({id:"123"}); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('fails the install if module error', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null }, - installModule: function() { - return Promise.reject(new Error("test error")); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Error: test error"); - done(); - }); - }); - it('fails the install if module not found', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null }, - installModule: function() { - var err = new Error("test error"); - err.code = 404; - return Promise.reject(err); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - }); - }); - describe('delete', function() { - it('returns 400 if settings are unavailable', function(done) { - initNodes({ - settings:{available:function(){return false}} - }); - - request(app) - .del('/nodes/123') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - describe('by module', function() { - it('uninstalls the module', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return {nodes:[{id:"123"}]} }, - getNodeInfo: function() { return null }, - uninstallModule: function() { return Promise.resolve({id:"123"});} - } - }); - request(app) - .del('/nodes/foo') - .expect(204) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('fails the uninstall if the module is not installed', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null }, - getNodeInfo: function() { return null } - } - }); - request(app) - .del('/nodes/foo') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('fails the uninstall if the module is not installed', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return {nodes:[{id:"123"}]} }, - getNodeInfo: function() { return null }, - uninstallModule: function() { return Promise.reject(new Error("test error"));} - } - }); - request(app) - .del('/nodes/foo') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Error: test error"); - done(); - }); - }); - }); - - }); - - describe('enable/disable', function() { - it('returns 400 if settings are unavailable', function(done) { - initNodes({ - settings:{available:function(){return false}} - }); - request(app) - .put('/nodes/123') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 400 for invalid node payload', function(done) { - initNodes({ - settings:{available:function(){return true}} - }); - request(app) - .put('/nodes/node-red/foo') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Invalid request"); - done(); - }); - }); - - it('returns 400 for invalid module payload', function(done) { - initNodes({ - settings:{available:function(){return true}} - }); - request(app) - .put('/nodes/foo') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Invalid request"); - - done(); - }); - }); - - it('returns 404 for unknown node', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return null } - } - }); - - request(app) - .put('/nodes/node-red/foo') - .send({enabled:false}) - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 404 for unknown module', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null } - } - }); - - request(app) - .put('/nodes/node-blue') - .send({enabled:false}) - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('enables disabled node', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: false} }, - enableNode: function() { return Promise.resolve({id:"123",enabled: true,types:['a']}); } - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",true); - - done(); - }); - }); - - it('disables enabled node', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: true} }, - disableNode: function() { return Promise.resolve({id:"123",enabled: false,types:['a']}); } - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:false}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",false); - - done(); - }); - }); - - describe('no-ops if already in the right state', function() { - function run(state,done) { - var enableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: true,types:['a']}) }); - var disableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: false,types:['a']}) }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: state} }, - enableNode: enableNode, - disableNode: disableNode - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.false(); - disableNodeCalled.should.be.false(); - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - - describe('does not no-op if err on node', function() { - function run(state,done) { - var enableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: true,types:['a']}) }); - var disableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: false,types:['a']}) }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: state, err:"foo"} }, - enableNode: enableNode, - disableNode: disableNode - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.equal(state); - disableNodeCalled.should.be.equal(!state); - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - - it('enables disabled module', function(done) { - var n1 = {id:"123",enabled:false,types:['a']}; - var n2 = {id:"456",enabled:false,types:['b']}; - var enableNode = sinon.stub(); - enableNode.onFirstCall().returns((function() { - n1.enabled = true; - return Promise.resolve(n1); - })()); - enableNode.onSecondCall().returns((function() { - n2.enabled = true; - return Promise.resolve(n2); - })()); - enableNode.returns(null); - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} }, - enableNode: enableNode - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",true); - res.body.nodes[1].should.have.property("enabled",true); - - done(); - }); - }); - - it('disables enabled module', function(done) { - var n1 = {id:"123",enabled:true,types:['a']}; - var n2 = {id:"456",enabled:true,types:['b']}; - var disableNode = sinon.stub(); - disableNode.onFirstCall().returns((function() { - n1.enabled = false; - return Promise.resolve(n1); - })()); - disableNode.onSecondCall().returns((function() { - n2.enabled = false; - return Promise.resolve(n2); - })()); - disableNode.returns(null); - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} }, - disableNode: disableNode - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:false}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",false); - res.body.nodes[1].should.have.property("enabled",false); - - done(); - }); - }); - - describe('no-ops if a node in module already in the right state', function() { - function run(state,done) { - var node = {id:"123",enabled:state,types:['a']}; - var enableNode = sinon.spy(function(id) { - node.enabled = true; - return Promise.resolve(node); - }); - var disableNode = sinon.spy(function(id) { - node.enabled = false; - return Promise.resolve(node); - }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[node]}; }, - enableNode: enableNode, - disableNode: disableNode - } - }); - request(app) - .put('/nodes/node-red') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.false(); - disableNodeCalled.should.be.false(); - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - - describe('does not no-op if err on a node in module', function() { - function run(state,done) { - var node = {id:"123",enabled:state,types:['a'],err:"foo"}; - var enableNode = sinon.spy(function(id) { - node.enabled = true; - return Promise.resolve(node); - }); - var disableNode = sinon.spy(function(id) { - node.enabled = false; - return Promise.resolve(node); - }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[node]}; }, - enableNode: enableNode, - disableNode: disableNode - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.equal(state); - disableNodeCalled.should.be.equal(!state); - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - }); - - describe('get icons', function() { - it('returns icon list', function(done) { - initNodes({ - nodes:{ - getNodeIcons: function() { - return {"module":["1.png","2.png","3.png"]}; - } - } - }); - request(app) - .get('/getIcons') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - console.log(res.body); - res.body.should.have.property("module"); - res.body.module.should.be.an.Array(); - res.body.module.should.have.lengthOf(3); - done(); - }); - }); - }); -}); - -*/ diff --git a/test/unit/@node-red/runtime/lib/api/plugins_spec.js b/test/unit/@node-red/runtime/lib/api/plugins_spec.js deleted file mode 100644 index 7ae6d8286..000000000 --- a/test/unit/@node-red/runtime/lib/api/plugins_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -const should = require("should"); -const sinon = require("sinon"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/api/plugins") - -const mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/plugins", function() { - const pluginList = [{id:"one",module:'test-module'},{id:"two",module:"node-red"}]; - const pluginConfigs = "123"; - - describe("getPluginList", function() { - it("gets the plugin list", function() { - plugins.init({ - log: mockLog(), - plugins: { - getPluginList: function() { return pluginList} - } - }); - return plugins.getPluginList({}).then(function(result) { - result.should.eql(pluginList); - }) - }); - }); - describe("getPluginConfigs", function() { - it("gets the plugin configs", function() { - plugins.init({ - log: mockLog(), - plugins: { - getPluginConfigs: function() { return pluginConfigs} - } - }); - return plugins.getPluginConfigs({}).then(function(result) { - result.should.eql(pluginConfigs); - }) - }); - }); - describe("getPluginCatalogs", function() { - it("gets the plugin catalogs", function() { - plugins.init({ - log: mockLog(), - plugins: { - getPluginList: function() { return pluginList} - }, - i18n: { - i: { - changeLanguage: function(lang,done) { done && done() }, - getResourceBundle: function(lang, id) { return {lang,id}} - } - } - }); - return plugins.getPluginCatalogs({lang: "en-US"}).then(function(result) { - JSON.stringify(result).should.eql(JSON.stringify({ one: { lang: "en-US", id: "one" } })) - }) - }); - }); - -}); \ No newline at end of file diff --git a/test/unit/@node-red/runtime/lib/api/projects_spec.js b/test/unit/@node-red/runtime/lib/api/projects_spec.js deleted file mode 100644 index 2fe7ca221..000000000 --- a/test/unit/@node-red/runtime/lib/api/projects_spec.js +++ /dev/null @@ -1,1170 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var projects = NR_TEST_UTILS.require("@node-red/runtime/lib/api/projects") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/settings", function() { - describe("available", function() { - it("resolves true if projects available", function(done) { - projects.init({ - storage: { - projects: {} - } - }); - projects.available().then(function(result) { - result.should.be.true(); - done(); - }).catch(done); - }) - it("resolves false if projects unavailable", function(done) { - projects.init({ - storage: { - } - }); - projects.available().then(function(result) { - result.should.be.false(); - done(); - }).catch(done); - }) - - }); - describe("listProjects", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - listProjects: sinon.spy(function(user) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve([1,2,3]); - } - }), - getActiveProject: function(user) { - if (user === "noActive") { - return null; - } - return {name:"aProject"}; - } - }}}; - before(function() { - projects.init(runtime); - }) - it("lists the projects, without an active project", function(done) { - projects.listProjects({user:"noActive"}).then(function(result) { - result.should.have.property('projects',[1,2,3]); - result.should.not.have.property('active'); - done(); - }).catch(done); - }); - it("lists the projects, with an active project", function(done) { - projects.listProjects({user:"foo"}).then(function(result) { - result.should.have.property('projects',[1,2,3]); - result.should.have.property('active','aProject'); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.listProjects({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - err.should.have.property('status',400); - done(); - }).catch(done); - }); - - }); - describe("createProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - createProject: sinon.spy(function(user,project) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve(project); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("create project", function(done) { - projects.createProject({user:"known",project:{a:1}}).then(function(result) { - result.should.eql({a:1}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.createProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("initialiseProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - initialiseProject: sinon.spy(function(user,id,project) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({id,project}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("intialise project", function(done) { - projects.initialiseProject({user:"known",id:123, project:{a:1}}).then(function(result) { - result.should.eql({id:123, project:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.initialiseProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getActiveProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getActiveProject: sinon.spy(function(user) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve("123"); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("returns active project", function(done) { - projects.getActiveProject({user:"known"}).then(function(result) { - result.should.eql("123"); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getActiveProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("setActiveProject", function() { - var activeProject; - var runtime; - beforeEach(function() { - runtime = { - log: mockLog(), - storage: {projects: { - getActiveProject: sinon.spy(function() { return activeProject;}), - setActiveProject: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - projects.init(runtime); - }) - it("sets project if current project is unset", function(done) { - activeProject = null; - projects.setActiveProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("sets project if current project is different", function(done) { - activeProject = {name:456}; - projects.setActiveProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("no-ops if current project is same", function(done) { - activeProject = {name:123}; - projects.setActiveProject({user:"known",id:123}).then(function(result) { - (result === undefined).should.be.true(); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.setActiveProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getProject: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("returns project", function(done) { - projects.getProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("updateProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - updateProject: sinon.spy(function(user,id,project) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,project}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("updates project", function(done) { - projects.updateProject({user:"known",id:123,project:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,project:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.updateProject({user:"error",id:123,project:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("deleteProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - deleteProject: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("deletes project", function(done) { - projects.deleteProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.deleteProject({user:"error",id:123}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getStatus", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getStatus: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets status", function(done) { - projects.getStatus({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getStatus({user:"error",id:123,project:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("getBranches", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getBranches: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets branches", function(done) { - projects.getBranches({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getBranches({user:"error",id:123,remote:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("getBranchStatus", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getBranchStatus: sinon.spy(function(user,id,branch) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,branch}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets branch status", function(done) { - projects.getBranchStatus({user:"known",id:123,branch:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,branch:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getBranchStatus({user:"error",id:123,branch:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("setBranch", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - setBranch: sinon.spy(function(user,id,branch,create) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,branch,create}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("commits", function(done) { - projects.setBranch({user:"known",id:123,branch:{a:1},create:true}).then(function(result) { - result.should.eql({user:"known",id:123,branch:{a:1},create:true}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.setBranch({user:"error",id:123,branch:{a:1},create:true}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("deleteBranch", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - deleteBranch: sinon.spy(function(user,id,branch,something,force) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,branch,force}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("commits", function(done) { - projects.deleteBranch({user:"known",id:123,branch:{a:1},force:true}).then(function(result) { - result.should.eql({user:"known",id:123,branch:{a:1},force:true}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.deleteBranch({user:"error",id:123,branch:{a:1},force:true}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("commit", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - commit: sinon.spy(function(user,id,message) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,message}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("commits", function(done) { - projects.commit({user:"known",id:123,message:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,message:{message:{a:1}}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.commit({user:"error",id:123,message:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("getCommit", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getCommit: sinon.spy(function(user,id,sha) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,sha}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets commit", function(done) { - projects.getCommit({user:"known",id:123,sha:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,sha:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getCommit({user:"error",id:123,sha:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getCommits", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getCommits: sinon.spy(function(user,id,options) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,options}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets commits with default limit/before", function(done) { - projects.getCommits({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123,options:{limit:20,before:undefined}}); - done(); - }).catch(done); - }); - it("gets commits with provided limit/before", function(done) { - projects.getCommits({user:"known",id:123,limit:10,before:456}).then(function(result) { - result.should.eql({user:"known",id:123,options:{limit:10,before:456}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getCommits({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("abortMerge", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - abortMerge: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("aborts merge", function(done) { - projects.abortMerge({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.abortMerge({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("resolveMerge", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - resolveMerge: sinon.spy(function(user,id,path,resolution) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path,resolution}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("resolves merge", function(done) { - projects.resolveMerge({user:"known",id:123,path:"/abc",resolution:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:"/abc",resolution:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.resolveMerge({user:"error",id:123,path:"/abc",resolution:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getFiles", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getFiles: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets files", function(done) { - projects.getFiles({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getFiles({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getFile: sinon.spy(function(user,id,path,tree) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path,tree}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets file", function(done) { - projects.getFile({user:"known",id:123,path:"/abc",tree:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:"/abc",tree:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getFile({user:"error",id:123,path:"/abc",tree:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("stageFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - stageFile: sinon.spy(function(user,id,path) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("stages a file", function(done) { - projects.stageFile({user:"known",id:123,path:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.stageFile({user:"error",id:123,path:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("unstageFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - unstageFile: sinon.spy(function(user,id,path) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("unstages a file", function(done) { - projects.unstageFile({user:"known",id:123,path:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.unstageFile({user:"error",id:123,path:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("revertFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - revertFile: sinon.spy(function(user,id,path) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("reverts a file", function(done) { - projects.revertFile({user:"known",id:123,path:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.revertFile({user:"error",id:123,path:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getFileDiff", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getFileDiff: sinon.spy(function(user,id,path,type) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path,type}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets file diff", function(done) { - projects.getFileDiff({user:"known",id:123,path:{a:1},type:"abc"}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1},type:"abc"}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getFileDiff({user:"error",id:123,path:{a:1},type:"abc"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getRemotes", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getRemotes: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets remotes", function(done) { - projects.getRemotes({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getRemotes({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("addRemote", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - addRemote: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("adds a remote", function(done) { - projects.addRemote({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.addRemote({user:"error",id:123,remote:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("removeRemote", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - removeRemote: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("removes a remote", function(done) { - projects.removeRemote({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.removeRemote({user:"error",id:123,remote:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("updateRemote", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - updateRemote: sinon.spy(function(user,id,name,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,name,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("updates a remote", function(done) { - projects.updateRemote({user:"known",id:123,remote:{name:"abc",a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,name:"abc",remote:{name:"abc",a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.updateRemote({user:"error",id:123,remote:{name:"abc",a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("pull", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - pull: sinon.spy(function(user,id,remote,track,allowUnrelatedHistories) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote,track,allowUnrelatedHistories}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("pulls", function(done) { - projects.pull({user:"known",id:123,remote:"abc",track:false,allowUnrelatedHistories:true}).then(function(result) { - result.should.eql({user:"known",id:123,remote:"abc",track:false,allowUnrelatedHistories:true}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.pull({user:"error",id:123,remote:"abc",track:false,allowUnrelatedHistories:true}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("push", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - push: sinon.spy(function(user,id,remote,track) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote,track}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("pulls", function(done) { - projects.push({user:"known",id:123,remote:"abc",track:false}).then(function(result) { - result.should.eql({user:"known",id:123,remote:"abc",track:false}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.push({user:"error",id:123,remote:"abc",track:false}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - -}); diff --git a/test/unit/@node-red/runtime/lib/api/settings_spec.js b/test/unit/@node-red/runtime/lib/api/settings_spec.js deleted file mode 100644 index 9b3b94229..000000000 --- a/test/unit/@node-red/runtime/lib/api/settings_spec.js +++ /dev/null @@ -1,988 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); -var clone = require("clone"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/api/settings") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/settings", function() { - describe("getRuntimeSettings", function() { - it("gets the runtime settings", function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => ["lib1"] }, - storage: {} - }) - return settings.getRuntimeSettings({}).then(result => { - result.should.have.property("httpNodeRoot","testHttpNodeRoot"); - result.should.have.property("version","testVersion"); - result.should.have.property("paletteCategories",["red","blue","green"]); - result.should.have.property("libraries",["lib1"]); - result.should.have.property("testNodeSetting","helloWorld"); - result.should.have.property("testPluginSettings","helloPluginWorld"); - result.should.not.have.property("foo",123); - result.should.have.property("flowEncryptionType","test-key-type"); - result.should.not.have.property("user"); - result.should.have.property("externalModules"); - result.externalModules.should.eql({palette:{allowInstall:false, allowUpload: false}}); - - }) - }); - it("gets the filtered user settings", function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: {} - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.have.property("user"); - result.user.should.have.property("username","nick"); - result.user.should.have.property("permissions","*"); - result.user.should.have.property("image","http://example.com"); - result.user.should.have.property("anonymous",false); - result.user.should.not.have.property("private"); - }) - }); - it("gets the filtered settings when editor disabled ", function() { - settings.init({ - settings: { - disableEditor: true, - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - getActiveProject: () => 'test-active-project', - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.have.property("user"); - result.user.should.have.property("username","nick"); - result.user.should.have.property("permissions","*"); - result.user.should.have.property("image","http://example.com"); - result.user.should.have.property("anonymous",false); - result.user.should.not.have.property("private"); - - // Filtered out when disableEditor is true - result.should.not.have.property("paletteCategories",["red","blue","green"]); - result.should.not.have.property("testNodeSetting","helloWorld"); - result.should.not.have.property("foo",123); - result.should.not.have.property("flowEncryptionType","test-key-type"); - result.should.not.have.property("project"); - result.should.not.have.property("git"); - - }) - }); - it('includes project settings if projects available', function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - getActiveProject: () => 'test-active-project', - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.have.property("project","test-active-project"); - result.should.not.have.property("files"); - result.should.have.property("git"); - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - }); - }); - - it('includes existing files details if projects enabled but no active project and files exist', function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - flowFileExists: () => true, - getActiveProject: () => false, - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - result.should.not.have.property("project"); - result.should.have.property("files"); - result.files.should.have.property("flow",'test-flow-file'); - result.files.should.have.property("credentials",'test-creds-file'); - result.should.have.property("git"); - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - }); - }); - - it('does not include file details if projects enabled but no active project and files do not exist', function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - flowFileExists: () => false, - getActiveProject: () => false, - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.not.have.property("project"); - result.should.not.have.property("files"); - result.should.have.property("git"); - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - }); - }); - }); - describe("getUserSettings", function() { - before(function() { - settings.init({ - settings: { - getUserSettings: username => username - } - }); - }) - it("returns default user settings", function() { - return settings.getUserSettings({}).then(result => { - result.should.eql("_"); - }) - }) - it("returns default user settings for anonymous", function() { - return settings.getUserSettings({user:{anonymous:true}}).then(result => { - result.should.eql("_"); - }) - }) - it("returns user settings", function() { - return settings.getUserSettings({user:{username:'nick'}}).then(result => { - result.should.eql("nick"); - }) - }) - }); - - describe("updateUserSettings", function() { - var userSettings; - before(function() { - settings.init({ - settings: { - getUserSettings: username => clone(userSettings[username]), - setUserSettings: (username, settings) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } else if (username === 'throw') { - throw new Error("thrown error"); - } - userSettings[username] = clone(settings); - return Promise.resolve(); - } - }, - log: mockLog() - }); - }) - beforeEach(function() { - userSettings = { - "_": { abc: 123 }, - "nick": {abc: 456} - } - }) - it('sets default user settings', function() { - return settings.updateUserSettings({settings:{abc:789}}).then(function() { - userSettings._.abc.should.eql(789) - }) - }) - it('merges user settings', function() { - return settings.updateUserSettings({settings:{def:789}}).then(function() { - userSettings._.abc.should.eql(123) - userSettings._.def.should.eql(789) - }) - }) - it('sets default user settings for anonymous user', function() { - return settings.updateUserSettings({user:{anonymous:true},settings:{def:789}}).then(function() { - userSettings._.abc.should.eql(123) - userSettings._.def.should.eql(789) - }) - }) - it('sets named user settings', function() { - return settings.updateUserSettings({user:{username:'nick'},settings:{def:789}}).then(function() { - userSettings.nick.abc.should.eql(456) - userSettings.nick.def.should.eql(789) - }) - }) - it('rejects with suitable error', function(done) { - settings.updateUserSettings({user:{username:'error'},settings:{def:789}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - it('rejects with suitable error - thrown', function(done) { - settings.updateUserSettings({user:{username:'throw'},settings:{def:789}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - describe("getUserKeys", function() { - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - listSSHKeys: username => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } - return Promise.resolve([username]) - } - } - } - } - }) - }) - it('returns the default users keys', function() { - return settings.getUserKeys({}).then(result => { - result.should.eql(['__default']); - }) - }) - it('returns the default users keys for anonymous', function() { - return settings.getUserKeys({user:{anonymous:true}}).then(result => { - result.should.eql(['__default']); - }) - }) - it('returns the users keys', function() { - return settings.getUserKeys({user:{username:'nick'}}).then(result => { - result.should.eql(['nick']); - }) - }) - it('rejects with suitable error', function(done) { - settings.getUserKeys({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - - describe("getUserKey", function() { - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - getSSHKey: (username, id) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } else if (username === '404') { - return Promise.resolve(null); - } - return Promise.resolve({username,id}) - } - } - } - } - }) - }) - it('returns the default user key', function() { - return settings.getUserKey({id:'keyid'}).then(result => { - result.should.eql({id:'keyid',username:"__default"}); - }) - }) - it('returns the default user key - anonymous', function() { - return settings.getUserKey({user:{anonymous:true},id:'keyid'}).then(result => { - result.should.eql({id:'keyid',username:"__default"}); - }) - }) - it('returns the user key', function() { - return settings.getUserKey({user:{username:'nick'},id:'keyid'}).then(result => { - result.should.eql({id:'keyid',username:"nick"}); - }) - }) - it('404s for unknown key', function(done) { - settings.getUserKey({user:{username:'404'},id:'keyid'}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 404); - err.should.have.property('code', 'not_found'); - done(); - }).catch(done); - }) - it('rejects with suitable error', function(done) { - settings.getUserKey({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - describe("generateUserKey", function() { - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - generateSSHKey: (username, opts) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } - return Promise.resolve(JSON.stringify({username,opts})) - } - } - } - } - }) - }) - it('generates for the default user', function() { - return settings.generateUserKey({id:'keyid'}).then(result => { - var data = JSON.parse(result); - data.should.eql({opts:{id:'keyid'},username:"__default"}); - }) - }) - it('generates for the default user - anonymous', function() { - return settings.generateUserKey({user:{anonymous:true},id:'keyid'}).then(result => { - var data = JSON.parse(result); - data.should.eql({opts:{user:{anonymous:true},id:'keyid'},username:"__default"}); - }) - }) - it('generates for the user', function() { - return settings.generateUserKey({user:{username:'nick'},id:'keyid'}).then(result => { - var data = JSON.parse(result); - data.should.eql({opts:{user:{username:'nick'},id:'keyid'},username:"nick"}); - }) - }) - it('rejects with suitable error', function(done) { - settings.generateUserKey({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - - }); - describe("removeUserKey", function() { - var received = {}; - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - deleteSSHKey: (username, id) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } - received.username = username; - received.id = id; - return Promise.resolve(); - } - } - } - } - }) - }); - beforeEach(function() { - received.username = ""; - received.id = ""; - }) - it('removes for the default user', function() { - return settings.removeUserKey({id:'keyid'}).then(() => { - received.username.should.eql("__default"); - received.id.should.eql("keyid"); - }) - }) - it('removes for the default user key - anonymous', function() { - return settings.removeUserKey({user:{anonymous:true},id:'keyid'}).then(() => { - received.username.should.eql("__default"); - received.id.should.eql("keyid"); - }) - }) - it('returns the user key', function() { - return settings.removeUserKey({user:{username:'nick'},id:'keyid'}).then(() => { - received.username.should.eql("nick"); - received.id.should.eql("keyid"); - }) - }) - it('rejects with suitable error', function(done) { - settings.removeUserKey({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - -}); - - - -/* - - -var should = require("should"); -var sinon = require("sinon"); -var request = require("supertest"); -var express = require("express"); -var editorApi = require("../../../../red/api/editor"); -var comms = require("../../../../red/api/editor/comms"); -var info = require("../../../../red/api/editor/settings"); -var auth = require("../../../../red/api/auth"); -var sshkeys = require("../../../../red/api/editor/sshkeys"); -var bodyParser = require("body-parser"); -var fs = require("fs-extra"); -var fspath = require("path"); - - -describe("api/editor/sshkeys", function() { - var app; - var mockList = [ - 'library','theme','locales','credentials','comms' - ] - var isStarted = true; - var errors = []; - var session_data = {}; - - var mockRuntime = { - settings:{ - httpNodeRoot: true, - httpAdminRoot: true, - disableEditor: false, - exportNodeSettings:function(){}, - storage: { - getSessions: function(){ - return Promise.resolve(session_data); - }, - setSessions: function(_session) { - session_data = _session; - return Promise.resolve(); - } - } - }, - log:{audit:function(){},error:function(msg){errors.push(msg)},trace:function(){}}, - storage: { - projects: { - ssh: { - init: function(){}, - listSSHKeys: function(){}, - getSSHKey: function(){}, - generateSSHKey: function(){}, - deleteSSHKey: function(){} - } - } - }, - events:{on:function(){},removeListener:function(){}}, - isStarted: function() { return isStarted; }, - nodes: {installerEnabled: function() { return false }} - }; - - before(function() { - auth.init(mockRuntime); - app = express(); - app.use(bodyParser.json()); - app.use(editorApi.init({},mockRuntime)); - }); - after(function() { - }) - - beforeEach(function() { - sinon.stub(mockRuntime.storage.projects.ssh, "listSSHKeys"); - sinon.stub(mockRuntime.storage.projects.ssh, "getSSHKey"); - sinon.stub(mockRuntime.storage.projects.ssh, "generateSSHKey"); - sinon.stub(mockRuntime.storage.projects.ssh, "deleteSSHKey"); - }) - afterEach(function() { - mockRuntime.storage.projects.ssh.listSSHKeys.restore(); - mockRuntime.storage.projects.ssh.getSSHKey.restore(); - mockRuntime.storage.projects.ssh.generateSSHKey.restore(); - mockRuntime.storage.projects.ssh.deleteSSHKey.restore(); - }) - - it('GET /settings/user/keys --- return empty list', function(done) { - mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve([])); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - res.body.keys.should.be.empty(); - done(); - }); - }); - - it('GET /settings/user/keys --- return normal list', function(done) { - var fileList = [ - 'test_key01', - 'test_key02' - ]; - var retList = fileList.map(function(elem) { - return { - name: elem - }; - }); - mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve(retList)); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - for (var item of retList) { - res.body.keys.should.containEql(item); - } - done(); - }); - }); - - it('GET /settings/user/keys --- return Error', function(done) { - var errInstance = new Error("Messages here....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.listSSHKeys.returns(p); - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return 404', function(done) { - mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null)); - request(app) - .get("/settings/user/keys/NOT_REAL") - .expect(404) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - it('GET /settings/user/keys --- return Unexpected Error', function(done) { - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.listSSHKeys.returns(p); - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return content', function(done) { - var key_file_name = "test_key"; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent)); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true(); - res.body.should.be.deepEqual({ publickey: fileContent }); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.getSSHKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.getSSHKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('POST /settings/user/keys --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name)); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - - it('POST /settings/user/keys --- return parameter error', function(done) { - var key_file_name = "test_key"; - mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name)); - request(app) - .post("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("You need to have body or body.name"); - done(); - }); - }); - - it('POST /settings/user/keys --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.generateSSHKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('POST /settings/user/keys --- return Unexpected error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.generateSSHKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true)); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(204) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.be.deepEqual({}); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); -}); -*/ diff --git a/test/unit/@node-red/runtime/lib/flows/Flow_spec.js b/test/unit/@node-red/runtime/lib/flows/Flow_spec.js deleted file mode 100644 index eed4810c4..000000000 --- a/test/unit/@node-red/runtime/lib/flows/Flow_spec.js +++ /dev/null @@ -1,1327 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require('sinon'); -var clone = require('clone'); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var flowUtils = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util"); -var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); - - -describe('Flow', function() { - var getType; - - var stoppedNodes = {}; - var stoppedOrder = []; - var currentNodes = {}; - var rewiredNodes = {}; - var createCount = 0; - - beforeEach(function() { - currentNodes = {}; - stoppedNodes = {}; - stoppedOrder =[]; - rewiredNodes = {}; - createCount = 0; - Flow.init({settings:{},log:{ - log: sinon.stub(), // function() { console.log("l",[...arguments].map(a => JSON.stringify(a)).join(" ")) },// - debug: sinon.stub(), // function() { console.log("d",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - trace: sinon.stub(), // function() { console.log("t",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - warn: sinon.stub(), // function() { console.log("w",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - info: sinon.stub(), // function() { console.log("i",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - }}); - }); - - var TestNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - // console.log(this.id,msg.payload); - node.handled++; - node.send(msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestNode,Node); - - var TestErrorNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.name = n.name; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.error("test error",msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestErrorNode,Node); - - var TestAsyncNode = function(n) { - Node.call(this,n); - var node = this; - this.scope = n.scope; - this.uncaught = n.uncaught; - this.foo = n.foo; - this.handled = 0; - this.messages = []; - this.stopped = false; - this.closeDelay = n.closeDelay || 50; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - msg.handled = node.handled; - node.messages.push(msg); - node.send(msg); - }); - this.on('close',function(done) { - setTimeout(function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - done(); - },node.closeDelay); - }); - } - util.inherits(TestAsyncNode,Node); - - var TestDoneNode = function(n) { - Node.call(this,n); - var node = this; - this.scope = n.scope; - this.uncaught = n.uncaught; - this.foo = n.foo; - this.handled = 0; - this.messages = []; - this.stopped = false; - this.closeDelay = n.closeDelay || 50; - currentNodes[node.id] = node; - this.on('input',function(msg, send, done) { - node.handled++; - node.messages.push(msg); - send(msg); - done(); - }); - this.on('close',function(done) { - setTimeout(function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - done(); - },node.closeDelay); - }); - } - util.inherits(TestDoneNode,Node); - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - if (type=="test") { - return TestNode; - } else if (type=="testError"){ - return TestErrorNode; - } else if (type=="testDone"){ - return TestDoneNode; - } else { - return TestAsyncNode; - } - }); - }); - after(function() { - getType.restore(); - }); - - describe('#constructor',function() { - it('called with an empty flow',function() { - var config = flowUtils.parseConfig([]); - var flow = Flow.create({},config); - - var nodeCount = 0; - Object.keys(flow.getActiveNodes()).length.should.equal(0); - }); - }); - - describe('#start',function() { - it("instantiates an initial configuration and stops it",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - flow.getNode('2').should.have.a.property('id','2'); - flow.getNode('3').should.have.a.property('id','3'); - flow.getNode('4').should.have.a.property('id','4'); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["2"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - - currentNodes["3"].on("input", function() { - currentNodes["1"].should.have.a.property("handled",1); - currentNodes["2"].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - - flow.stop().then(function() { - try { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - currentNodes.should.not.have.a.property("4"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - stoppedNodes.should.have.a.property("4"); - done(); - } catch(err) { - done(err); - } - }); - }); - currentNodes["1"].receive({payload:"test"}); - }); - - it("instantiates config nodes in the right order",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"5"}, // This node depends on #5 - {id:"5",z:"t1",type:"test"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(5); - - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - currentNodes.should.have.a.property("5"); - - currentNodes["1"].should.have.a.property("_index",2); - currentNodes["2"].should.have.a.property("_index",3); - currentNodes["3"].should.have.a.property("_index",4); - currentNodes["4"].should.have.a.property("_index",1); - currentNodes["5"].should.have.a.property("_index",0); - - flow.stop().then(function() { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - currentNodes.should.not.have.a.property("4"); - currentNodes.should.not.have.a.property("5"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - stoppedNodes.should.have.a.property("4"); - stoppedNodes.should.have.a.property("5"); - done(); - }); - }); - - - it("detects dependency loops in config nodes",function() { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5 - {id:"node2",z:"t1",type:"test",foo:"node1"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - /*jshint immed: false */ - (function(){ - flow.start(); - }).should.throw("Circular config node dependency detected: node1"); - }); - - it("rewires nodes specified by diff",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} - ]); - - var flow = Flow.create({},config,config.flows["t1"]); - createCount.should.equal(0); - flow.start(); - //TODO: use update to pass in new wiring and verify the change - createCount.should.equal(3); - flow.start({rewired:["2"]}); - createCount.should.equal(3); - rewiredNodes.should.have.a.property("2"); - done(); - }); - - it("instantiates a node with environment variable property values",function(done) { - after(function() { - delete process.env.NODE_RED_TEST_VALUE; - }) - process.env.NODE_RED_TEST_VALUE = "a-value"; - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE)",wires:[]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:{a:"$(NODE_RED_TEST_VALUE)"},wires:[]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:" $(NODE_RED_TEST_VALUE)",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE) ",wires:[]}, - {id:"5",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE_NONE)",wires:[]}, - {id:"6",x:10,y:10,z:"t1",type:"test",foo:["$(NODE_RED_TEST_VALUE)"],wires:[]} - ]); - var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].foo.should.equal("a-value"); - activeNodes["2"].foo.a.should.equal("a-value"); - activeNodes["3"].foo.should.equal(" $(NODE_RED_TEST_VALUE)"); - activeNodes["4"].foo.should.equal("$(NODE_RED_TEST_VALUE) "); - activeNodes["5"].foo.should.equal("$(NODE_RED_TEST_VALUE_NONE)"); - activeNodes["6"].foo[0].should.equal("a-value"); - - flow.stop().then(function() { - done(); - }); - }); - - it("ignores disabled nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",d:true,type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"}, - {id:"5",z:"t1",type:"test",d:true,foo:"a"} - - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(3); - - flow.getNode('1').should.have.a.property('id','1'); - should.not.exist(flow.getNode('2')); - flow.getNode('3').should.have.a.property('id','3'); - flow.getNode('4').should.have.a.property('id','4'); - should.not.exist(flow.getNode('5')); - - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - // Message doesn't reach 3 as 2 is disabled - currentNodes["3"].should.have.a.property("handled",0); - - flow.stop().then(function() { - try { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - currentNodes.should.not.have.a.property("4"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.not.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - stoppedNodes.should.have.a.property("4"); - done(); - } catch(err) { - done(err); - } - }); - },50); - }); - - }); - - describe('#stop', function() { - - - it("stops all nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"asyncTest",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - - flow.stop().then(function() { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - done(); - }).catch(done); - }); - - it("stops specified nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - - flow.stop(["2"]).then(function() { - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.have.a.property("3"); - stoppedNodes.should.not.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.not.have.a.property("3"); - done(); - }); - }); - - it("stops config nodes last",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"c1",z:"t1",type:"test"}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"c2",z:"t1",type:"test"}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"c3",z:"t1",type:"test"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("c1"); - currentNodes.should.have.a.property("c2"); - currentNodes.should.have.a.property("c3"); - stoppedOrder.should.have.a.length(0); - - flow.stop().then(function() { - stoppedOrder.should.eql([ '1', '2', '3', 'c1', 'c2', 'c3' ]); - done(); - }).catch(done); - }); - - - it("Times out a node that fails to close", function(done) { - Flow.init({settings:{nodeCloseTimeout:50},log:{ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - }}); - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"testAsync",closeDelay: 80, foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - - flow.stop().then(function() { - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - stoppedNodes.should.not.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - setTimeout(function() { - currentNodes.should.not.have.a.property("1"); - stoppedNodes.should.have.a.property("1"); - done(); - },40) - }); - }); - - }); - - describe('#getNode',function() { - it("gets a node known to the flow",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - - flow.stop().then(() => { done() }); - }); - - it("passes to parent if node not known locally",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({ - getNode: id => { return {id:id}} - },config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - - flow.getNode('parentNode').should.have.a.property('id','parentNode'); - - - flow.stop().then(() => { done() }); - }); - - it("does not pass to parent if cancelBubble set",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({ - getNode: id => { return {id:id}} - },config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - - should.not.exist(flow.getNode('parentNode',true)); - flow.stop().then(() => { done() }); - }); - }); - - describe("#handleStatus",function() { - it("passes a status event to the adjacent status node",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(5); - - - flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status",random:"otherProperty"}); - - setTimeout(function() { - - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","my-status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","1"); - statusMessage.status.source.should.have.a.property("type","test"); - statusMessage.status.source.should.have.a.property("name","a"); - - currentNodes["sn2"].should.have.a.property("handled",1); - statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","my-status"); - statusMessage.status.should.have.a.property("random","otherProperty"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","1"); - statusMessage.status.source.should.have.a.property("type","test"); - statusMessage.status.source.should.have.a.property("name","a"); - - - flow.stop().then(function() { - done(); - }); - },50) - }); - it("passes a status event to the adjacent scoped status node ",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",scope:["2"],foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"status",scope:["1"],foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(5); - - - flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",0); - currentNodes["sn2"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","my-status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","1"); - statusMessage.status.source.should.have.a.property("type","test"); - statusMessage.status.source.should.have.a.property("name","a"); - - - flow.stop().then(function() { - done(); - }); - },50); - }); - - }); - - describe("#handleError",function() { - it("passes an error event to the adjacent catch node",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}, - {id:"sn3",x:10,y:10,z:"t1",type:"catch",uncaught:true,wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(6); - - - flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - currentNodes["sn2"].should.have.a.property("handled",1); - statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - // Node sn3 has uncaught:true - so should not get called - currentNodes["sn3"].should.have.a.property("handled",0); - - - flow.stop().then(function() { - done(); - }); - },50); - }); - it("passes an error event to the adjacent scoped catch node ",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",scope:["2"],foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"catch",scope:["1"],foo:"a",wires:[]}, - {id:"sn3",x:10,y:10,z:"t1",type:"catch",uncaught:true,wires:[]}, - {id:"sn4",x:10,y:10,z:"t1",type:"catch",uncaught:true,wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(7); - - flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",0); - currentNodes["sn2"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - // Node sn3/4 have uncaught:true - so should not get called - currentNodes["sn3"].should.have.a.property("handled",0); - currentNodes["sn4"].should.have.a.property("handled",0); - - // Inject error that sn1/2 will ignore - so should get picked up by sn3 - flow.handleError(config.flows["t1"].nodes["3"],"my-error-2",{a:"foo-2"}); - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",0); - currentNodes["sn2"].should.have.a.property("handled",1); - currentNodes["sn3"].should.have.a.property("handled",1); - currentNodes["sn4"].should.have.a.property("handled",1); - - statusMessage = currentNodes["sn3"].messages[0]; - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error-2"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","3"); - statusMessage.error.source.should.have.a.property("type","test"); - - flow.stop().then(function() { - done(); - }); - },50); - },50); - }); - it("moves any existing error object sideways",function(done){ - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo",error:"existing"}); - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("_error","existing"); - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - flow.stop().then(function() { - done(); - }); - },50); - }); - it("prevents an error looping more than 10 times",function(){}); - }); - - describe("#handleComplete",function() { - it("passes a complete event to the adjacent Complete node",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"testDone",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"testDone",foo:"a",wires:[]}, - {id:"cn",x:10,y:10,z:"t1",type:"complete",scope:["1","3"],foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(4); - - var msg = {payload: "hello world"} - var n1 = currentNodes["1"].receive(msg); - setTimeout(function() { - currentNodes["cn"].should.have.a.property("handled",2); - currentNodes["cn"].messages[0].should.have.a.property("handled",1); - currentNodes["cn"].messages[1].should.have.a.property("handled",2); - flow.stop().then(function() { - done(); - }); - },50); - }); - }); - - - describe("#send", function() { - it("sends a message - no cloning", function(done) { - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - var messageReceived = false; - n2.receive = function(msg) { - messageReceived = true; - try { - msg.should.be.exactly(message); - shutdownTest(); - } catch(err) { - shutdownTest(err); - } - } - - var message = {payload:"hello"} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: false - }]) - messageReceived.should.be.false() - }) - it("sends a message - cloning", function(done) { - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - - n2.receive = function(msg) { - try { - // Message should be cloned - msg.should.be.eql(message); - msg.should.not.be.exactly(message); - shutdownTest(); - } catch(err) { - shutdownTest(err); - } - } - - var message = {payload:"hello"} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - }) - it("sends multiple messages", function(done) { - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - - var messageCount = 0; - n2.receive = function(msg) { - try { - msg.should.be.exactly(messages[messageCount++]); - if (messageCount === 2) { - shutdownTest(); - } - } catch(err) { - shutdownTest(err); - } - } - - var messages = [{payload:"hello"},{payload:"world"}]; - - flow.send([{ - msg: messages[0], - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined } - },{ - msg: messages[1], - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined } - }]) - }) - it("sends a message - triggers hooks", function(done) { - var hookErrors = []; - var messageReceived = false; - var hooksCalled = []; - hooks.add("onSend", function(sendEvents) { - hooksCalled.push("onSend") - try { - messageReceived.should.be.false() - sendEvents.should.have.length(1); - sendEvents[0].msg.should.be.exactly(message); - } catch(err) { - hookErrors.push(err); - } - }) - hooks.add("preRoute", function(sendEvent) { - hooksCalled.push("preRoute") - try { - messageReceived.should.be.false() - sendEvent.msg.should.be.exactly(message); - should.not.exist(sendEvent.destination.node) - } catch(err) { - hookErrors.push(err); - } - - }) - hooks.add("preDeliver", function(sendEvent) { - hooksCalled.push("preDeliver") - try { - messageReceived.should.be.false() - // Cloning should have happened - sendEvent.msg.should.not.be.exactly(message); - // Destinatino node populated - should.exist(sendEvent.destination.node) - } catch(err) { - hookErrors.push(err); - } - - }) - hooks.add("postDeliver", function(sendEvent) { - hooksCalled.push("postDeliver") - try { - messageReceived.should.be.false() - - } catch(err) { - hookErrors.push(err); - } - - }) - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - n2.receive = function(msg) { - messageReceived = true; - try { - msg.should.be.eql(message); - msg.should.not.be.exactly(message); - hooksCalled.should.eql(["onSend","preRoute","preDeliver","postDeliver"]) - if (hookErrors.length > 0) { - shutdownTest(hookErrors[0]) - } else { - shutdownTest(); - } - } catch(err) { - shutdownTest(err); - } - } - - var message = {payload:"hello"} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - }) - - describe("errors thrown by hooks are reported to the sending node", function() { - var flow; - var n1,n2; - var messageReceived = false; - var errorReceived = null; - before(function() { - hooks.add("onSend", function(sendEvents) { - if (sendEvents[0].msg.payload === "trigger-onSend") { - throw new Error("onSend Error"); - } - }) - hooks.add("preRoute", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preRoute") { - throw new Error("preRoute Error"); - } - }) - hooks.add("preDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preDeliver") { - throw new Error("preDeliver Error"); - } - }) - hooks.add("postDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-postDeliver") { - throw new Error("postDeliver Error"); - } - }) - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - n1 = flow.getNode('1'); - n2 = flow.getNode('2'); - n2.receive = function(msg) { - messageReceived = true; - } - n1.error = function(err) { - errorReceived = err; - } - - }) - after(function(done) { - hooks.clear(); - flow.stop().then(() => { done() }); - }) - beforeEach(function() { - messageReceived = false; - errorReceived = null; - }) - function testHook(hook, msgExpected, done) { - var message = {payload:"trigger-"+hook} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - setTimeout(function() { - try { - messageReceived.should.equal(msgExpected); - should.exist(errorReceived) - errorReceived.toString().should.containEql(hook); - done(); - } catch(err) { - done(err); - } - },10) - } - - it("onSend", function(done) { testHook("onSend", false, done) }) - it("preRoute", function(done) { testHook("preRoute", false, done) }) - it("preDeliver", function(done) { testHook("preDeliver", false, done) }) - it("postDeliver", function(done) { testHook("postDeliver", true, done) }) - }) - - describe("hooks can stop the sending of messages", function() { - var flow; - var n1,n2; - var messageReceived = false; - var errorReceived = false; - before(function() { - hooks.add("onSend", function(sendEvents) { - if (sendEvents[0].msg.payload === "trigger-onSend") { - return false - } - }) - hooks.add("preRoute", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preRoute") { - return false - } - }) - hooks.add("preDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preDeliver") { - return false - } - }) - hooks.add("postDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-postDeliver") { - return false - } - }) - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - n1 = flow.getNode('1'); - n2 = flow.getNode('2'); - n2.receive = function(msg) { - messageReceived = true; - } - n1.error = function(err) { - errorReceived = true; - } - - }) - after(function(done) { - hooks.clear(); - flow.stop().then(() => { done() }); - }) - function testSend(payload,messageReceivedExpected,errorReceivedExpected,done) { - messageReceived = false; - errorReceived = false; - flow.send([{ - msg: {payload: payload}, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - setTimeout(function() { - try { - messageReceived.should.eql(messageReceivedExpected) - errorReceived.should.eql(errorReceivedExpected) - done(); - } catch(err) { - done(err); - } - },10) - } - function testHook(hook, done) { - testSend("pass",true,false,err => { - if (err) { - done(err) - } else { - testSend("trigger-"+hook,false,false,done); - } - }) - } - - it("onSend", function(done) { testHook("onSend", done) }) - it("preRoute", function(done) { testHook("preRoute", done) }) - it("preDeliver", function(done) { testHook("preDeliver", done) }) - // postDeliver happens after delivery is scheduled so cannot stop it - // it("postDeliver", function(done) { testHook("postDeliver", done) }) - }) - }) - - describe("#env", function () { - it("can instantiate a node with environment variable property values of group and tab", function (done) { - try { - after(function() { - delete process.env.V0; - delete process.env.V1; - }) - process.env.V0 = "gv0"; - process.env.V1 = "gv1"; - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab",env:[ - {"name": "V0", value: "v0", type: "str"} - ]}, - {id:"g1",type:"group",z:"t1",env:[ - {"name": "V0", value: "v1", type: "str"}, - {"name": "V1", value: "v2", type: "str"} - ]}, - {id:"g2",type:"group",z:"t1",g:"g1",env:[ - {"name": "V1", value: "v3", type: "str"} - ]}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"$(V0)",wires:[]}, - {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V0)",wires:[]}, - {id:"3",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]}, - {id:"4",x:10,y:10,z:"t1",g:"g2",type:"test",foo:"$(V1)",wires:[]}, - {id:"5",x:10,y:10,z:"t1",type:"test",foo:"$(V1)",wires:[]}, - ]); - var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].foo.should.equal("v0"); - activeNodes["2"].foo.should.equal("v1"); - activeNodes["3"].foo.should.equal("v2"); - activeNodes["4"].foo.should.equal("v3"); - activeNodes["5"].foo.should.equal("gv1"); - - flow.stop().then(function() { - done(); - }); - } - catch (e) { - console.log(e.stack); - done(e); - } - - }); - it("can access environment variable property using $parent", function (done) { - try { - after(function() { - delete process.env.V0; - delete process.env.V1; - }) - process.env.V0 = "gv0"; - process.env.V1 = "gv1"; - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab",env:[ - {"name": "V0", value: "v0", type: "str"} - ]}, - {id:"g1",type:"group",z:"t1",env:[ - {"name": "V0", value: "v1", type: "str"}, - {"name": "V1", value: "v2", type: "str"} - ]}, - {id:"g2",type:"group",z:"t1",g:"g1",env:[ - {"name": "V1", value: "v3", type: "str"} - ]}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V0}",wires:[]}, - {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"${$parent.V0}",wires:[]}, - {id:"3",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"${$parent.V1}",wires:[]}, - {id:"4",x:10,y:10,z:"t1",g:"g2",type:"test",foo:"${$parent.V1}",wires:[]}, - {id:"5",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V1}",wires:[]}, - ]); - var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].foo.should.equal("gv0"); - activeNodes["2"].foo.should.equal("v0"); - activeNodes["3"].foo.should.equal("gv1"); - activeNodes["4"].foo.should.equal("v2"); - activeNodes["5"].foo.should.equal("gv1"); - - flow.stop().then(function() { - done(); - }); - } - catch (e) { - console.log(e.stack); - done(e); - } - - }); - - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/flows/Subflow_spec.js b/test/unit/@node-red/runtime/lib/flows/Subflow_spec.js deleted file mode 100644 index 0479b5117..000000000 --- a/test/unit/@node-red/runtime/lib/flows/Subflow_spec.js +++ /dev/null @@ -1,885 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require('sinon'); -var clone = require('clone'); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var Subflow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Subflow"); -var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); - -var flowUtils = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); - -describe('Subflow', function() { - var getType; - - var stoppedNodes = {}; - var currentNodes = {}; - var rewiredNodes = {}; - var createCount = 0; - - beforeEach(function() { - currentNodes = {}; - stoppedNodes = {}; - rewiredNodes = {}; - createCount = 0; - var runtime = { - settings:{}, - log:{ - log: sinon.stub(), // function() { console.log("l",[...arguments].map(a => JSON.stringify(a)).join(" ")) },// - debug: sinon.stub(), // function() { console.log("d",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - trace: sinon.stub(), // function() { console.log("t",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - warn: sinon.stub(), // function() { console.log("w",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - info: sinon.stub(), // function() { console.log("i",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - } - } - Flow.init(runtime); - Subflow.init(runtime); - }); - - var TestNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - this.received = null; - currentNodes[node.id] = node; - this.on('input',function(msg) { - // console.log(this.id,msg.payload); - node.handled++; - node.received = msg.payload; - node.send(msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestNode,Node); - - var TestErrorNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.name = n.name; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.error("test error",msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestErrorNode,Node); - - - var TestStatusNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.name = n.name; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.status({text:"test status"}); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestStatusNode,Node); - - var TestAsyncNode = function(n) { - Node.call(this,n); - var node = this; - this.scope = n.scope; - this.foo = n.foo; - this.handled = 0; - this.messages = []; - this.stopped = false; - this.closeDelay = n.closeDelay || 50; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.messages.push(msg); - node.send(msg); - }); - this.on('close',function(done) { - setTimeout(function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - done(); - },node.closeDelay); - }); - } - util.inherits(TestAsyncNode,Node); - - var TestEnvNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.foo = n.foo; - var node = this; - this.stopped = false; - this.received = null; - currentNodes[node.id] = node; - this.on('input',function(msg) { - var flow = node._flow; - var val = flow.getSetting("__KEY__"); - node.received = val; - node.send({payload: val}); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestEnvNode,Node); - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - if (type=="test") { - return TestNode; - } else if (type=="testError"){ - return TestErrorNode; - } else if (type=="testStatus"){ - return TestStatusNode; - } else if (type=="testEnv"){ - return TestEnvNode; - } else { - return TestAsyncNode; - } - }); - }); - after(function() { - getType.restore(); - }); - describe('#start',function() { - it("instantiates a subflow and stops it",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3","4"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]},{"wires":[{"id":"sf1","port":0}]}]}, - {id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]}, - {id:"sf1-2",type:"test","z":"sf1",foo:"sf1-cn",x:166,y:99,"wires":[[]]}, - {id:"sf1-cn",type:"test","z":"sf1"} - ]); - var flow = Flow.create({handleError: (a,b,c) => { console.log(a,b,c); }},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(4); - // var sfInstanceId = Object.keys(activeNodes)[5]; - // var sfInstanceId2 = Object.keys(activeNodes)[6]; - var sfConfigId = Object.keys(activeNodes)[4]; - - flow.getNode('1').should.have.a.property('id','1'); - flow.getNode('2').should.have.a.property('id','2'); - flow.getNode('3').should.have.a.property('id','3'); - flow.getNode('4').should.have.a.property('id','4'); - // flow.getNode(sfInstanceId).should.have.a.property('id',sfInstanceId); - // flow.getNode(sfInstanceId2).should.have.a.property('id',sfInstanceId2); - // flow.getNode(sfConfigId).should.have.a.property('id',sfConfigId); - - // flow.getNode(sfInstanceId2).should.have.a.property('foo',sfConfigId); - - Object.keys(currentNodes).should.have.length(6); - - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - // currentNodes.should.have.a.property(sfInstanceId); - // currentNodes.should.have.a.property(sfInstanceId2); - // currentNodes.should.have.a.property(sfConfigId); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - currentNodes["4"].should.have.a.property("handled",0); - // currentNodes[sfInstanceId].should.have.a.property("handled",0); - // currentNodes[sfInstanceId2].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - // currentNodes[sfInstanceId].should.have.a.property("handled",1); - // currentNodes[sfInstanceId2].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - currentNodes["4"].should.have.a.property("handled",1); - - - - flow.stop().then(function() { - Object.keys(currentNodes).should.have.length(0); - Object.keys(stoppedNodes).should.have.length(6); - - // currentNodes.should.not.have.a.property("1"); - // currentNodes.should.not.have.a.property("3"); - // currentNodes.should.not.have.a.property("4"); - // // currentNodes.should.not.have.a.property(sfInstanceId); - // // currentNodes.should.not.have.a.property(sfInstanceId2); - // // currentNodes.should.not.have.a.property(sfConfigId); - // stoppedNodes.should.have.a.property("1"); - // stoppedNodes.should.have.a.property("3"); - // stoppedNodes.should.have.a.property("4"); - // // stoppedNodes.should.have.a.property(sfInstanceId); - // // stoppedNodes.should.have.a.property(sfInstanceId2); - // // stoppedNodes.should.have.a.property(sfConfigId); - done(); - }); - },150); - }); - it("instantiates a subflow inside a subflow and stops it",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3","4"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 1","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]}]}, - {id:"sf2",type:"subflow","name":"Subflow 2","info":"", - "in":[{wires:[]}],"out":[{"wires":[{"id":"sf2","port":0}]}]}, - {id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]}, - {id:"sf1-2",type:"subflow:sf2","z":"sf1",x:166,y:99,"wires":[[]]} - - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - flow.stop().then(function() { - Object.keys(currentNodes).should.have.length(0); - done(); - }); - },150); - }); - it("rewires a subflow node on update/start",function(done){ - - var rawConfig = [ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]}]}, - {id:"sf1-1",type:"test1","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]}, - {id:"sf1-2",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]} - ]; - - var config = flowUtils.parseConfig(clone(rawConfig)); - - rawConfig[2].wires = [["4"]]; - - var newConfig = flowUtils.parseConfig(rawConfig); - var diff = flowUtils.diffConfigs(config,newConfig); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(4); - // var sfInstanceId = Object.keys(activeNodes)[4]; - // var sfInstanceId2 = Object.keys(activeNodes)[5]; - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - currentNodes["4"].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - // currentNodes[sfInstanceId].should.have.a.property("handled",1); - // currentNodes[sfInstanceId2].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - currentNodes["4"].should.have.a.property("handled",0); - - flow.update(newConfig,newConfig.flows["t1"]); - flow.start(diff) - - currentNodes["1"].receive({payload:"test2"}); - setTimeout(function() { - - currentNodes["1"].should.have.a.property("handled",2); - // currentNodes[sfInstanceId].should.have.a.property("handled",2); - // currentNodes[sfInstanceId2].should.have.a.property("handled",2); - currentNodes["3"].should.have.a.property("handled",1); - currentNodes["4"].should.have.a.property("handled",1); - - - flow.stop().then(function() { - done(); - }); - },150); - },150); - }); - }); - describe('#stop', function() { - it("stops subflow instance nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[[]]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(3); - Object.keys(stoppedNodes).should.have.length(0); - flow.stop(["2"]).then(function() { - Object.keys(currentNodes).should.have.length(2); - Object.keys(stoppedNodes).should.have.length(1); - done(); - }).catch(done); - }); - }); - describe("#handleStatus",function() { - it("passes a status event to the subflow's parent tab status node - all scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","test status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("type","testStatus"); - statusMessage.status.source.should.have.a.property("name","test-status-node"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",scope:["2"],wires:[]} - ]); - var parentFlowStatusCalled = false; - - var flow = Flow.create({handleStatus:() => { parentFlowStatusCalled = true} },config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - parentFlowStatusCalled.should.be.false(); - - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","test status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("type","testStatus"); - statusMessage.status.source.should.have.a.property("name","test-status-node"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - }); - - describe("status node", function() { - it("emits a status event when a message is passed to a subflow-status node - msg.payload as string", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[{id:"sf1-1", port:0}]} - }, - {id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test-payload"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","test-payload"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","2"); - statusMessage.status.source.should.have.a.property("type","subflow:sf1"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[{id:"sf1-1", port:0}]} - }, - {id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:{text:"payload-obj"}}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","payload-obj"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","2"); - statusMessage.status.source.should.have.a.property("type","subflow:sf1"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - it("emits a status event when a message is passed to a subflow-status node - msg.status", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[{id:"sf1-1", port:0}]} - }, - {id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({status:{text:"status-obj"}}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","status-obj"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","2"); - statusMessage.status.source.should.have.a.property("type","subflow:sf1"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - it("does not emit a regular status event if it contains a subflow-status node", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[]} - }, - {id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test-payload"}); - - currentNodes["sn"].should.have.a.property("handled",0); - - flow.stop().then(function() { - - done(); - }); - }); - }) - - describe("#handleError",function() { - it("passes an error event to the subflow's parent tab catch node - all scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",name:"test-error-node",type:"testError","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","test error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("type","testError"); - statusMessage.error.source.should.have.a.property("name","test-error-node"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",name:"test-error-node",type:"testError","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",scope:["2"],wires:[]} - ]); - var parentFlowErrorCalled = false; - var flow = Flow.create({handleError:() => { parentFlowErrorCalled = true} },config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - parentFlowErrorCalled.should.be.false(); - - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","test error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("type","testError"); - statusMessage.error.source.should.have.a.property("name","test-error-node"); - - flow.stop().then(function() { - done(); - }); - },150); - - }); - }); - - describe("#env var", function() { - // should be changed according to internal env var representation - function setEnv(node, key, val) { - var flow = node._flow; - if (flow) { - var env = flow.env; - if (!env) { - env = flow.env = {}; - } - env[key] = { - name: key, - type: "str", - value: val - }; - } - } - - it("can access process env var", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, - {id:"sf1",type:"subflow",name:"Subflow 2",info:"", - "in":[ {wires:[{id:"sf1-1"}]} ], - "out":[ {wires:[{id:"sf1-2",port:0}]} ]}, - {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, - {id:"sf1-2",type:"testEnv",z:"sf1",foo:"sf1-cn",x:166,y:99,wires:[[]]} - ]); - var flow = Flow.create({ - getSetting: k=> process.env[k], - handleError: (a,b,c) => { console.log(a,b,c); } - },config,config.flows["t1"]); - - flow.start(); - - process.env["__KEY__"] = "__VAL__"; - - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL__"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - - it("can access subflow env var", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, - {id:"sf1",type:"subflow",name:"Subflow 2",info:"", - "in":[ {wires:[{id:"sf1-1"}]} ], - "out":[ {wires:[{id:"sf1-2",port:0}]} ]}, - {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, - {id:"sf1-2",type:"testEnv",z:"sf1",foo:"sf1.2",x:166,y:99,wires:[[]]} - ]); - var flow = Flow.create({ - getSetting: k=> process.env[k], - handleError: (a,b,c) => { console.log(a,b,c); } - },config,config.flows["t1"]); - - flow.start(); - - var testenv_node = null; - for (var n in currentNodes) { - var node = currentNodes[n]; - if (node.type === "testEnv") { - testenv_node = node; - break; - } - } - process.env["__KEY__"] = "__VAL0__"; - setEnv(testenv_node, "__KEY__", "__VAL1__"); - - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL1__"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - - it("can access nested subflow env var", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, - {id:"sf1",type:"subflow",name:"Subflow 1",info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-2",port:0}]}]}, - {id:"sf2",type:"subflow",name:"Subflow 2",info:"", - in:[{wires:[{id:"sf2-1"}]}], - out:[{wires:[{id:"sf2-2",port:0}]}]}, - {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, - {id:"sf1-2",type:"subflow:sf2",z:"sf1",x:166,y:99,wires:[[]]}, - {id:"sf2-1",type:"test",z:"sf2",foo:"sf2.1",x:166,y:99,wires:[["sf2-2"]]}, - {id:"sf2-2",type:"testEnv",z:"sf2",foo:"sf2.2",x:166,y:99,wires:[[]]}, - ]); - var flow = Flow.create({ - getSetting: k=> process.env[k], - handleError: (a,b,c) => { console.log(a,b,c); } - },config,config.flows["t1"]); - - flow.start(); - - var node_sf1_1 = null; - var node_sf2_1 = null; - var testenv_node = null; - for (var n in currentNodes) { - var node = currentNodes[n]; - if (node.foo === "sf1.1") { - node_sf1_1 = node; - } - if (node.foo === "sf2.1") { - node_sf2_1 = node; - } - } - - process.env["__KEY__"] = "__VAL0__"; - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL0__"); - - setEnv(node_sf1_1, "__KEY__", "__VAL1__"); - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL1__"); - - setEnv(node_sf2_1, "__KEY__", "__VAL2__"); - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL2__"); - - flow.stop().then(function() { - done(); - }); - },150); - },150); - },150); - }); - - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/flows/index_spec.js b/test/unit/@node-red/runtime/lib/flows/index_spec.js deleted file mode 100644 index 737846100..000000000 --- a/test/unit/@node-red/runtime/lib/flows/index_spec.js +++ /dev/null @@ -1,669 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var clone = require("clone"); -var NR_TEST_UTILS = require("nr-test-utils"); - -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var RedNode = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var RED = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes"); -var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); -var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry") -var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); - -describe('flows/index', function() { - - var storage; - var eventsOn; - var credentialsClean; - var credentialsLoad; - var credentialsAdd; - - var flowCreate; - var getType; - var checkFlowDependencies; - - var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - } - - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - return type.indexOf('missing') === -1; - }); - checkFlowDependencies = sinon.stub(typeRegistry, "checkFlowDependencies").callsFake(async function(flow) { - if (flow[0].id === "node-with-missing-modules") { - throw new Error("Missing module"); - } - }); - }); - - after(function() { - getType.restore(); - checkFlowDependencies.restore(); - }); - - - beforeEach(function() { - eventsOn = sinon.spy(events,"on"); - credentialsClean = sinon.stub(credentials,"clean").callsFake(function(conf) { - conf.forEach(function(n) { - delete n.credentials; - }); - return Promise.resolve(); - }); - credentialsLoad = sinon.stub(credentials,"load").callsFake(function(creds) { - if (creds && creds.hasOwnProperty("$") && creds['$'] === "fail") { - return Promise.reject("creds error"); - } - return Promise.resolve(); - }); - credentialsAdd = sinon.stub(credentials,"add").callsFake(async function(id, conf){}) - flowCreate = sinon.stub(Flow,"create").callsFake(function(parent, global, flow) { - var id; - if (typeof flow === 'undefined') { - flow = global; - id = '_GLOBAL_'; - } else { - id = flow.id; - } - flowCreate.flows[id] = { - flow: flow, - global: global, - start: sinon.spy(), - update: sinon.spy(), - stop: sinon.spy(), - getActiveNodes: function() { - return flow.nodes||{}; - }, - handleError: sinon.spy(), - handleStatus: sinon.spy() - - } - return flowCreate.flows[id]; - }); - flowCreate.flows = {}; - - storage = { - saveFlows: function(conf) { - storage.conf = conf; - return Promise.resolve(); - } - } - }); - - afterEach(function(done) { - eventsOn.restore(); - credentialsClean.restore(); - credentialsLoad.restore(); - credentialsAdd.restore(); - flowCreate.restore(); - - flows.stopFlows().then(done); - - }); - // describe('#init',function() { - // it('registers the type-registered handler', function() { - // flows.init({},{}); - // eventsOn.calledOnce.should.be.true(); - // }); - // }); - - describe('#setFlows', function() { - it('sets the full flow', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - credentialsClean.called.should.be.true(); - storage.hasOwnProperty('conf').should.be.true(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - - }); - it('loads the full flow for type load', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var loadStorage = { - saveFlows: function(conf) { - loadStorage.conf = conf; - return Promise.resolve(456); - }, - getFlows: function() { - return Promise.resolve({flows:originalConfig,rev:123}) - } - } - flows.init({log:mockLog, settings:{},storage:loadStorage}); - flows.setFlows(originalConfig,"load").then(function() { - credentialsClean.called.should.be.false(); - // 'load' type does not trigger a save - loadStorage.hasOwnProperty('conf').should.be.false(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - - }); - - it('extracts credentials from the full flow', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[],credentials:{"a":1}}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - credentialsClean.called.should.be.true(); - storage.hasOwnProperty('conf').should.be.true(); - var cleanedFlows = flows.getFlows(); - storage.conf.flows.should.eql(cleanedFlows.flows); - cleanedFlows.flows.should.not.eql(originalConfig); - cleanedFlows.flows[0].credentials = {"a":1}; - cleanedFlows.flows.should.eql(originalConfig); - done(); - }); - }); - - it('sets the full flow including credentials', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var credentials = {"t1-1":{"a":1}}; - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig,credentials).then(function() { - credentialsClean.called.should.be.true(); - credentialsAdd.called.should.be.true(); - credentialsAdd.lastCall.args[0].should.eql("t1-1"); - credentialsAdd.lastCall.args[1].should.eql({"a":1}); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - }); - - it('updates existing flows with partial deployment - nodes', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var newConfig = clone(originalConfig); - newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); - newConfig.push({id:"t2",type:"tab"}); - newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - events.once('flows:started',function() { - flows.setFlows(newConfig,"nodes").then(function() { - flows.getFlows().flows.should.eql(newConfig); - flowCreate.flows['t1'].update.called.should.be.true(); - flowCreate.flows['t2'].start.called.should.be.true(); - flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); - done(); - }) - }); - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - flows.startFlows(); - }); - }); - - it('updates existing flows with partial deployment - flows', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var newConfig = clone(originalConfig); - newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); - newConfig.push({id:"t2",type:"tab"}); - newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - events.once('flows:started',function() { - flows.setFlows(newConfig,"nodes").then(function() { - flows.getFlows().flows.should.eql(newConfig); - flowCreate.flows['t1'].update.called.should.be.true(); - flowCreate.flows['t2'].start.called.should.be.true(); - flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); - flows.stopFlows().then(done); - }) - }); - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - flows.startFlows(); - }); - }); - - it('returns error if it cannot decrypt credentials', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var credentials = {"$":"fail"}; - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig,credentials).then(function() { - done("Unexpected success when credentials couldn't be decrypted") - }).catch(function(err) { - done(); - }); - }); - }); - - describe('#load', function() { - it('loads the flow config', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - credentialsLoad.called.should.be.true(); - // 'load' type does not trigger a save - storage.hasOwnProperty('conf').should.be.false(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - }); - }); - - describe('#startFlows', function() { - it('starts the loaded config', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - events.once('flows:started',function() { - Object.keys(flowCreate.flows).should.eql(['_GLOBAL_','t1']); - done(); - }); - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }); - }); - it('does not start if nodes missing', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(() => { - try { - flowCreate.called.should.be.false(); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('starts when missing nodes registered', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - {id:"t1-3",x:10,y:10,z:"t1",type:"missing2",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(() => { - flowCreate.called.should.be.false(); - events.emit("type-registered","missing"); - setTimeout(function() { - flowCreate.called.should.be.false(); - events.emit("type-registered","missing2"); - setTimeout(function() { - flowCreate.called.should.be.true(); - done(); - },10); - },10); - }); - }); - - it('does not start if external modules missing', function(done) { - var originalConfig = [ - {id:"node-with-missing-modules",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - var receivedEvent = null; - var handleEvent = function(payload) { - receivedEvent = payload; - } - - events.on("runtime-event",handleEvent); - - //{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});" - - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(flows.startFlows).then(() => { - events.removeListener("runtime-event",handleEvent); - try { - flowCreate.called.should.be.false(); - receivedEvent.should.have.property('id','runtime-state'); - receivedEvent.should.have.property('payload', - { error: 'missing-modules', - type: 'warning', - text: 'notification.warnings.missing-modules', - modules: [] } - ); - - done(); - }catch(err) { - done(err) - } - }); - }); - - }); - - describe.skip('#get',function() { - - }); - - describe('#eachNode', function() { - it('iterates the flow nodes', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - var c = 0; - flows.eachNode(function(node) { - c++ - }) - c.should.equal(2); - done(); - }); - }); - }); - - describe('#stopFlows', function() { - - }); - // describe('#handleError', function() { - // it('passes error to correct flow', function(done) { - // var originalConfig = [ - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - // {id:"t1",type:"tab"} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleError(originalConfig[0],"message",{}); - // flowCreate.flows['t1'].handleError.called.should.be.true(); - // done(); - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // it('passes error to flows that use the originating global config', function(done) { - // var originalConfig = [ - // {id:"configNode",type:"test"}, - // {id:"t1",type:"tab"}, - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, - // {id:"t2",type:"tab"}, - // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, - // {id:"t3",type:"tab"}, - // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleError(originalConfig[0],"message",{}); - // try { - // flowCreate.flows['t1'].handleError.called.should.be.true(); - // flowCreate.flows['t2'].handleError.called.should.be.false(); - // flowCreate.flows['t3'].handleError.called.should.be.true(); - // done(); - // } catch(err) { - // done(err); - // } - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // }); - // describe('#handleStatus', function() { - // it('passes status to correct flow', function(done) { - // var originalConfig = [ - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - // {id:"t1",type:"tab"} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleStatus(originalConfig[0],"message"); - // flowCreate.flows['t1'].handleStatus.called.should.be.true(); - // done(); - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // - // it('passes status to flows that use the originating global config', function(done) { - // var originalConfig = [ - // {id:"configNode",type:"test"}, - // {id:"t1",type:"tab"}, - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, - // {id:"t2",type:"tab"}, - // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, - // {id:"t3",type:"tab"}, - // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleStatus(originalConfig[0],"message"); - // try { - // flowCreate.flows['t1'].handleStatus.called.should.be.true(); - // flowCreate.flows['t2'].handleStatus.called.should.be.false(); - // flowCreate.flows['t3'].handleStatus.called.should.be.true(); - // done(); - // } catch(err) { - // done(err); - // } - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // }); - - describe('#checkTypeInUse', function() { - - before(function() { - sinon.stub(typeRegistry,"getNodeInfo").callsFake(function(id) { - if (id === 'unused-module') { - return {types:['one','two','three']} - } else { - return {types:['one','test','three']} - } - }); - }); - - after(function() { - typeRegistry.getNodeInfo.restore(); - }); - - it('returns cleanly if type not is use', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - flows.checkTypeInUse("unused-module"); - done(); - }); - }); - it('throws error if type is in use', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - /*jshint immed: false */ - try { - flows.checkTypeInUse("used-module"); - done("type_in_use error not thrown"); - } catch(err) { - err.code.should.eql("type_in_use"); - done(); - } - }); - }); - }); - - describe('#addFlow', function() { - it("rejects duplicate node id",function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - flows.addFlow({ - label:'new flow', - nodes:[ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]} - ] - }).then(function() { - done(new Error('failed to reject duplicate node id')); - }).catch(function(err) { - done(); - }) - }); - - }); - - it("addFlow",function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - storage.setFlows = function() { - return Promise.resolve(); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(function() { - flows.addFlow({ - label:'new flow', - nodes:[ - {id:"t2-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2-2",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2-3",z:"t1",type:"test"} - ] - }).then(function(id) { - flows.getFlows().flows.should.have.lengthOf(6); - var createdFlows = Object.keys(flowCreate.flows); - createdFlows.should.have.lengthOf(3); - createdFlows[2].should.eql(id); - done(); - }).catch(function(err) { - done(err); - }) - }); - - }); - }) - describe('#updateFlow', function() { - it.skip("updateFlow"); - }) - describe('#removeFlow', function() { - it.skip("removeFlow"); - }) - describe('#disableFlow', function() { - it.skip("disableFlow"); - }) - describe('#enableFlow', function() { - it.skip("enableFlow"); - }) -}); diff --git a/test/unit/@node-red/runtime/lib/flows/util_spec.js b/test/unit/@node-red/runtime/lib/flows/util_spec.js deleted file mode 100644 index 6a4571e87..000000000 --- a/test/unit/@node-red/runtime/lib/flows/util_spec.js +++ /dev/null @@ -1,801 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var clone = require("clone"); -var NR_TEST_UTILS = require("nr-test-utils"); -var flowUtil = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); -var redUtil = NR_TEST_UTILS.require("@node-red/util").util; - -describe('flows/util', function() { - var getType; - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - return type!=='missing'; - }); - }); - after(function() { - getType.restore(); - }); - - describe('#mapEnvVarProperties',function() { - before(function() { - process.env.foo1 = "bar1"; - process.env.foo2 = "bar2"; - process.env.foo3 = "bar3"; - }) - after(function() { - delete process.env.foo1; - delete process.env.foo2; - delete process.env.foo3; - }) - it('handles ENV substitutions in an object - $()', function() { - var foo = {a:"$(foo1)",b:"$(foo2)",c:{d:"$(foo3)"}}; - for (var p in foo) { - if (foo.hasOwnProperty(p)) { - flowUtil.mapEnvVarProperties(foo,p,{getSetting: p => process.env[p]}); - } - } - foo.should.eql({ a: 'bar1', b: 'bar2', c: { d: 'bar3' } } ); - }); - it('handles ENV substitutions in an object - ${}', function() { - var foo = {a:"${foo1}",b:"${foo2}",c:{d:"${foo3}"}}; - for (var p in foo) { - if (foo.hasOwnProperty(p)) { - flowUtil.mapEnvVarProperties(foo,p,{getSetting: p => process.env[p]}); - } - } - foo.should.eql({ a: 'bar1', b: 'bar2', c: { d: 'bar3' } } ); - }); - - it('gets ENV from parent flow', function() { - var foo = {a:"$(unknown)",b:"$(foo2)",c:{d:"$(foo3)"}}; - for (var p in foo) { - if (foo.hasOwnProperty(p)) { - flowUtil.mapEnvVarProperties(foo,p,{ - getSetting: name => name[0]==='f'?name.toUpperCase():undefined - }); - } - } - foo.should.eql({ a: '$(unknown)', b: 'FOO2', c: { d: 'FOO3' } } ); - }); - }); - describe('#getEnvVar',function() { - before(function() { - process.env.foo1 = "bar1"; - }) - after(function() { - delete process.env.foo1; - }) - it('returns a known env var', function() { - flowUtil.init({settings:{}}); - flowUtil.getEnvVar("foo1").should.equal("bar1") - }) - it('returns undefined for an unknown env var', function() { - flowUtil.init({settings:{}}); - (flowUtil.getEnvVar("foo2") === undefined).should.be.true() - }) - it('returns undefined for an excluded env var', function() { - flowUtil.init({settings:{envVarExcludes:['foo1']}}); - (flowUtil.getEnvVar("foo1") === undefined).should.be.true() - }) - - }); - - describe('#diffNodes',function() { - it('handles a null old node', function() { - flowUtil.diffNodes(null,{}).should.be.true(); - }); - it('ignores x/y changes', function() { - flowUtil.diffNodes({x:10,y:10},{x:20,y:10}).should.be.false(); - flowUtil.diffNodes({x:10,y:10},{x:10,y:20}).should.be.false(); - }); - it('ignores wiring changes', function() { - flowUtil.diffNodes({wires:[]},{wires:[1,2,3]}).should.be.false(); - }); - it('spots existing property change - string', function() { - flowUtil.diffNodes({a:"foo"},{a:"bar"}).should.be.true(); - }); - it('spots existing property change - number', function() { - flowUtil.diffNodes({a:0},{a:1}).should.be.true(); - }); - it('spots existing property change - boolean', function() { - flowUtil.diffNodes({a:true},{a:false}).should.be.true(); - }); - it('spots existing property change - truthy', function() { - flowUtil.diffNodes({a:true},{a:1}).should.be.true(); - }); - it('spots existing property change - falsey', function() { - flowUtil.diffNodes({a:false},{a:0}).should.be.true(); - }); - it('spots existing property change - array', function() { - flowUtil.diffNodes({a:[0,1,2]},{a:[0,2,3]}).should.be.true(); - }); - it('spots existing property change - object', function() { - flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{a:[0,2,3]}}).should.be.true(); - flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{b:[0,1,2]}}).should.be.true(); - }); - it('spots added property', function() { - flowUtil.diffNodes({a:"foo"},{a:"foo",b:"bar"}).should.be.true(); - }); - it('spots removed property', function() { - flowUtil.diffNodes({a:"foo",b:"bar"},{a:"foo"}).should.be.true(); - }); - - - }); - - describe('#parseConfig',function() { - - it('parses a single-tab flow', function() { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a single-tab flow with global config node', function() { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",foo:"cn", wires:[]}, - {id:"cn",type:"test"}, - {id:"t1",type:"tab"} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a multi-tab flow', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2",type:"tab"}, - {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t2":{"id":"t2","type":"tab"},"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}},"t2":{"id":"t2","type":"tab","subflows":{},"configs":{},"nodes":{"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a subflow flow', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"subflow:sf1",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",x:10,y:10,z:"sf1",type:"test",wires:[]} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[]},"sf1":{"id":"sf1","type":"subflow"},"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"subflows":{"sf1":{"id":"sf1","type":"subflow","configs":{},"nodes":{"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"instances":[{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}]}},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a flow with a missing type', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"sf1",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - parsedConfig.missingTypes.should.eql(['missing']); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},"t1-2":{"id":"t1-2","x":10,"y":10,"z":"t1","type":"missing","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},'t1-2': { id: 't1-2', x: 10, y: 10, z: 't1', type: 'missing', wires: [] }}}},"groups":{},"missingTypes":["missing"]}; - redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true(); - }); - - it('parses a flow with a missing flow', function() { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",foo:"cn", wires:[]}, - {id:"cn",type:"test"}, - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a flow including a group', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"g1",type:"group",z:"t1"} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"g1":{"id":"g1","type":"group","z":"t1"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"groups":{"g1":{"id":"g1","type":"group","z":"t1"}},"missingTypes":[]} - parsedConfig.should.eql(expectedConfig); - }); - - }); - - describe('#diffConfigs', function() { - - it('handles an identical configuration', function() { - var config = [{id:"123",type:"test",foo:"a",wires:[]}]; - - var originalConfig = flowUtil.parseConfig(clone(config)); - var changedConfig = flowUtil.parseConfig(clone(config)); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.have.length(0); - }); - - it('identifies nodes with changed properties, including downstream linked', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[[1]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[0].foo = "b"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.eql(["1"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.eql(["2"]); - - }); - it('identifies nodes with changed properties, including upstream linked', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].bar = "c"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.eql(["2"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.eql(["1"]); - }); - - it('identifies nodes with changed credentials, including downstream linked', function() { - var config = [{id:"1",type:"test",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[0].credentials = {}; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.eql(["1"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.eql(["2"]); - }); - - it('identifies nodes with changed wiring', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].wires[0][0] = "3"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.eql(["2"]); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('identifies nodes with changed wiring - second connection added', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].wires[0].push("1"); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.eql(["2"]); - diffResult.linked.sort().should.eql(["1"]); - }); - - it('identifies nodes with changed wiring - node connected', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[["2"]]},{id:"2",type:"test",bar:"b",wires:[[]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].wires.push("3"); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.eql(["2"]); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('identifies new nodes', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig.push({id:"2",type:"test",bar:"b",wires:[["1"]]}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.eql(["2"]); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1"]); - }); - - it('identifies deleted nodes', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[["2"]]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig.splice(1,1); - newConfig[0].wires = []; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.eql(["2"]); - diffResult.rewired.should.eql(["1"]); - diffResult.linked.sort().should.eql(["3"]); - }); - - it('identifies config nodes changes, node->config', function() { - var config = [ - {id:"1",type:"test",foo:"configNode",wires:[["2"]]}, - {id:"2",type:"test",bar:"b",wires:[["3"]]}, - {id:"3",type:"test",foo:"a",wires:[]}, - {id:"configNode",type:"testConfig"} - ]; - var newConfig = clone(config); - newConfig[3].foo = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(["1","configNode"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["2","3"]); - }); - - it('identifies config nodes changes, node->config->config', function() { - var config = [ - {id:"1",type:"test",foo:"configNode1",wires:[["2"]]}, - {id:"2",type:"test",bar:"b",wires:[["3"]]}, - {id:"3",type:"test",foo:"a",wires:[]}, - {id:"configNode1",foo:"configNode2",type:"testConfig"}, - {id:"configNode2",type:"testConfig"} - ]; - var newConfig = clone(config); - newConfig[4].foo = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(["1","configNode1","configNode2"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["2","3"]); - }); - - it('marks a parent subflow as changed for an internal property change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",foo:"a",wires:[]}, - {id:"4",type:"subflow:sf1",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[4].foo = "b"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', '4', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - - - }); - - it('marks a parent subflow as changed for an internal wiring change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[4].wires = [["sf1-2"]]; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal node add', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig.push({id:"sf1-3",z:"sf1",type:"test",wires:[]}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - - }); - - it('marks a parent subflow as changed for an internal node delete', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig.splice(5,1); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(1); - diffResult.removed.sort().should.eql(['sf1-2']); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - input removed', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].in[0].wires = []; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - input added', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].in[0].wires.push({"id":"sf1-2"}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - output added', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].out[0].wires.push({"id":"sf1-2","port":0}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - output removed', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].out[0].wires = []; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for a global config node change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",prop:"configNode",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]}, - {id:"configNode",a:"foo",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[6].a = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', "configNode", 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow instance change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf2",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"subflow:sf2",wires:[]}, - {id:"sf2-1",z:"sf2",type:"test",wires:[]}, - {id:"sf2-2",z:"sf2",type:"test",wires:[]}, - ]; - - var newConfig = clone(config); - newConfig[8].a = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1', 'sf2']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - - it('ignores tab changes that are immaterial', function() { - var config = [{id:"1",type:"tab",label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"}]; - var newConfig = clone(config); - newConfig[0].label = "barney"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - }); - - - it('marks a deleted tab as removed', function() { - var config = [{id:"f1",type:"tab",label:"fred"},{id:"n1",type:"test",bar:"b",wires:[["1"]],z:"f1"}, - {id:"f2",type:"tab",label:"fred"},{id:"n2",type:"test",bar:"b",wires:[["1"]],z:"f2"}]; - var newConfig = clone(config); - newConfig = newConfig.slice(0,2); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.sort().should.eql(['f2', 'n2']); - diffResult.rewired.should.have.length(0); - }); - - it('marks all nodes as added when tab state changes disabled to enabled', function() { - var config = [{id:"1",type:"tab",disabled:true,label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"},{id:"3",type:"test"}]; - var newConfig = clone(config); - newConfig[0].disabled = false; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(2); - diffResult.added.sort().should.eql(["1","2"]); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - }); - it('marks all nodes as removed when tab state changes enabled to disabled', function() { - var config = [{id:"1",type:"tab",disabled:false,label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"},{id:"3",type:"test"}]; - var newConfig = clone(config); - newConfig[0].disabled = true; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(2); - diffResult.removed.sort().should.eql(["1","2"]); - diffResult.rewired.should.have.length(0); - }); - - it('marks a node as removed when its state changes enabled to disabled', function() { - var config = [{id:"1",type:"tab",disabled:false,label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"},{id:"3",type:"test"}]; - var newConfig = clone(config); - newConfig[1].d = true; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(2); - diffResult.changed.sort().should.eql(["1","2"]); - diffResult.removed.should.have.length(1); - diffResult.removed.sort().should.eql(["2"]); - diffResult.rewired.should.have.length(0); - }); - - }); -}); diff --git a/test/unit/@node-red/runtime/lib/index_spec.js b/test/unit/@node-red/runtime/lib/index_spec.js deleted file mode 100644 index 9041ba66e..000000000 --- a/test/unit/@node-red/runtime/lib/index_spec.js +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var api = NR_TEST_UTILS.require("@node-red/runtime/lib/api"); -var runtime = NR_TEST_UTILS.require("@node-red/runtime"); - -var redNodes = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes"); -var storage = NR_TEST_UTILS.require("@node-red/runtime/lib/storage"); -var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/settings"); -var util = NR_TEST_UTILS.require("@node-red/util"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("runtime", function() { - afterEach(function() { - if (console.log.restore) { - console.log.restore(); - } - }) - - before(function() { - process.env.NODE_RED_HOME = NR_TEST_UTILS.resolve("node-red"); - }); - after(function() { - delete process.env.NODE_RED_HOME; - }); - function mockUtil(metrics) { - sinon.stub(log,"log").callsFake(function(){}) - sinon.stub(log,"warn").callsFake(function(){}) - sinon.stub(log,"info").callsFake(function(){}) - sinon.stub(log,"trace").callsFake(function(){}) - sinon.stub(log,"metric").callsFake(function(){ return !!metrics }) - sinon.stub(log,"_").callsFake(function(){ return "abc"}) - sinon.stub(i18n,"registerMessageCatalog").callsFake(function(){ return Promise.resolve()}) - } - function unmockUtil() { - log.log.restore && log.log.restore(); - log.warn.restore && log.warn.restore(); - log.info.restore && log.info.restore(); - log.trace.restore && log.trace.restore(); - log.metric.restore && log.metric.restore(); - log._.restore && log._.restore(); - i18n.registerMessageCatalog.restore && i18n.registerMessageCatalog.restore(); - } - describe("init", function() { - beforeEach(function() { - sinon.stub(log,"init").callsFake(function() {}); - sinon.stub(settings,"init").callsFake(function() {}); - sinon.stub(redNodes,"init").callsFake(function() {}) - mockUtil(); - }); - afterEach(function() { - log.init.restore(); - settings.init.restore(); - redNodes.init.restore(); - unmockUtil(); - }) - - it("initialises components", function() { - runtime.init({testSettings: true, httpAdminRoot:"/"}); - settings.init.called.should.be.true(); - redNodes.init.called.should.be.true(); - }); - - it("returns version", function() { - runtime.init({testSettings: true, httpAdminRoot:"/"}); - return runtime.version().then(version => { - /^\d+\.\d+\.\d+(-.*)?$/.test(version).should.be.true(); - }); - - - }) - }); - - describe("start",function() { - var storageInit; - var settingsLoad; - var redNodesInit; - var redNodesLoad; - var redNodesCleanModuleList; - var redNodesGetNodeList; - var redNodesLoadFlows; - var redNodesStartFlows; - var redNodesLoadContextsPlugin; - - beforeEach(function() { - storageInit = sinon.stub(storage,"init").callsFake(function(settings) {return Promise.resolve();}); - redNodesInit = sinon.stub(redNodes,"init").callsFake(function() {}); - redNodesLoad = sinon.stub(redNodes,"load").callsFake(function() {return Promise.resolve()}); - redNodesCleanModuleList = sinon.stub(redNodes,"cleanModuleList").callsFake(function(){}); - redNodesLoadFlows = sinon.stub(redNodes,"loadFlows").callsFake(function() {return Promise.resolve()}); - redNodesStartFlows = sinon.stub(redNodes,"startFlows").callsFake(function() {}); - redNodesLoadContextsPlugin = sinon.stub(redNodes,"loadContextsPlugin").callsFake(function() {return Promise.resolve()}); - mockUtil(); - }); - afterEach(function() { - storageInit.restore(); - redNodesInit.restore(); - redNodesLoad.restore(); - redNodesGetNodeList.restore(); - redNodesCleanModuleList.restore(); - redNodesLoadFlows.restore(); - redNodesStartFlows.restore(); - redNodesLoadContextsPlugin.restore(); - unmockUtil(); - }); - it("reports errored/missing modules",function(done) { - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function(cb) { - return [ - { err:"errored",name:"errName" }, // error - { module:"module",enabled:true,loaded:false,types:["typeA","typeB"]} // missing - ].filter(cb); - }); - runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}}); - // sinon.stub(console,"log"); - runtime.start().then(function() { - // console.log.restore(); - try { - storageInit.calledOnce.should.be.true(); - redNodesInit.calledOnce.should.be.true(); - redNodesLoad.calledOnce.should.be.true(); - redNodesLoadFlows.calledOnce.should.be.true(); - - log.warn.calledWithMatch("Failed to register 1 node type"); - log.warn.calledWithMatch("Missing node modules"); - log.warn.calledWithMatch(" - module: typeA, typeB"); - redNodesCleanModuleList.calledOnce.should.be.true(); - done(); - } catch(err) { - done(err); - } - }).catch(err=>{done(err)}); - }); - it("initiates load of missing modules",function(done) { - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function(cb) { - return [ - { err:"errored",name:"errName" }, // error - { err:"errored",name:"errName" }, // error - { module:"module",enabled:true,loaded:false,types:["typeA","typeB"]}, // missing - { module:"node-red",enabled:true,loaded:false,types:["typeC","typeD"]} // missing - ].filter(cb); - }); - var serverInstallModule = sinon.stub(redNodes,"installModule").callsFake(function(name) { return Promise.resolve({nodes:[]});}); - runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}}); - sinon.stub(console,"log"); - runtime.start().then(function() { - console.log.restore(); - try { - log.warn.calledWithMatch("Failed to register 2 node types"); - log.warn.calledWithMatch("Missing node modules"); - log.warn.calledWithMatch(" - module: typeA, typeB"); - log.warn.calledWithMatch(" - node-red: typeC, typeD"); - redNodesCleanModuleList.calledOnce.should.be.false(); - serverInstallModule.calledOnce.should.be.true(); - serverInstallModule.calledWithMatch("module"); - done(); - } catch(err) { - done(err); - } finally { - serverInstallModule.restore(); - } - }).catch(err=>{done(err)}); - }); - it("reports errored modules when verbose is enabled",function(done) { - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function(cb) { - return [ - { err:"errored",name:"errName" } // error - ].filter(cb); - }); - runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}}); - sinon.stub(console,"log"); - runtime.start().then(function() { - console.log.restore(); - try { - log.warn.neverCalledWithMatch("Failed to register 1 node type"); - log.warn.calledWithMatch("[errName] errored"); - done(); - } catch(err) { - done(err); - } - }).catch(err=>{done(err)}); - }); - - it("reports runtime metrics",function(done) { - var stopFlows = sinon.stub(redNodes,"stopFlows").callsFake(function() { return Promise.resolve();} ); - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function() {return []}); - unmockUtil(); - mockUtil(true); - runtime.init( - {testSettings: true, runtimeMetricInterval:200, httpAdminRoot:"/", load:function() { return Promise.resolve();}}, - {}, - undefined); - // sinon.stub(console,"log"); - runtime.start().then(function() { - // console.log.restore(); - setTimeout(function() { - try { - log.log.args.should.have.lengthOf(3); - log.log.args[0][0].should.have.property("event","runtime.memory.rss"); - log.log.args[1][0].should.have.property("event","runtime.memory.heapTotal"); - log.log.args[2][0].should.have.property("event","runtime.memory.heapUsed"); - done(); - } catch(err) { - done(err); - } finally { - runtime.stop(); - stopFlows.restore(); - } - },300); - }).catch(err=>{done(err)}); - }); - - - }); - - it("stops components", function(done) { - var stopFlows = sinon.stub(redNodes,"stopFlows").callsFake(function() { return Promise.resolve();} ); - var closeContextsPlugin = sinon.stub(redNodes,"closeContextsPlugin").callsFake(function() { return Promise.resolve();} ); - runtime.stop().then(function(){ - stopFlows.called.should.be.true(); - closeContextsPlugin.called.should.be.true(); - stopFlows.restore(); - closeContextsPlugin.restore(); - done(); - }).catch(function(err){ - stopFlows.restore(); - closeContextsPlugin.restore(); - return done(err) - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/library/examples_spec.js b/test/unit/@node-red/runtime/lib/library/examples_spec.js deleted file mode 100644 index d7f466d83..000000000 --- a/test/unit/@node-red/runtime/lib/library/examples_spec.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var fs = require("fs"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - -describe("runtime/library/examples", function() { - describe("getEntry", function() { - before(function() { - examplesLibrary.init({ - log: mockLog, - storage: { - getLibraryEntry: function(type,path) { - return Promise.resolve({type,path}); - }, - getFlow: function(path) { - return Promise.resolve({path}); - } - }, - nodes: { - getNodeExampleFlows: function() { - return { - "test-module": { - f: ["abc"] - }, - "@scope/test-module": { - f: ["abc","throw"] - } - - } - }, - getNodeExampleFlowPath: function(module,entryPath) { - if (module === "unknown") { - return null; - } - return "/tmp/"+module+"/"+entryPath; - } - } - }); - sinon.stub(fs,"readFile").callsFake(function(path,opts,callback) { - if (path === "/tmp/test-module/abc") { - callback(null,"Example flow result"); - } else if (path === "/tmp/@scope/test-module/abc") { - callback(null,"Example scope flow result"); - } else if (path === "/tmp/test-module/throw") { - throw new Error("Instant error") - } else { - callback(new Error("Unexpected path:"+path)) - } - }) - }); - after(function() { - fs.readFile.restore(); - }) - - it ('returns a flow example entry', function(done) { - examplesLibrary.getEntry("flows","test-module/abc").then(function(result) { - result.should.eql("Example flow result"); - done(); - }).catch(done); - }); - - it ('returns a flow example listing - top level', function(done) { - examplesLibrary.getEntry("flows","").then(function(result) { - result.should.eql([ 'test-module', '@scope/test-module' ]) - done(); - }).catch(done); - }); - it ('returns a flow example listing - in module', function(done) { - examplesLibrary.getEntry("flows","test-module").then(function(result) { - result.should.eql([{ fn: 'abc' }]) - done(); - }).catch(done); - }); - it ('returns a flow example listing - in scoped module', function(done) { - examplesLibrary.getEntry("flows","@scope/test-module").then(function(result) { - result.should.eql([{ fn: 'abc' }, {fn: 'throw'}]) - done(); - }).catch(done); - }); - it ('returns a flow example entry from scoped module', function(done) { - examplesLibrary.getEntry("flows","@scope/test-module/abc").then(function(result) { - result.should.eql("Example scope flow result"); - done(); - }).catch(done); - }); - it ('returns an error for unknown flow example entry', function(done) { - examplesLibrary.getEntry("flows","unknown/abc").then(function(result) { - done(new Error("No error thrown")) - }).catch(function(err) { - err.should.have.property("code","not_found"); - done(); - }); - }); - it ('returns an error for file load error - async', function(done) { - examplesLibrary.getEntry("flows","test-module/unknown").then(function(result) { - done(new Error("No error thrown")) - }).catch(function(err) { - done(); - }); - }); - it ('returns an error for file load error - sync', function(done) { - examplesLibrary.getEntry("flows","test-module/throw").then(function(result) { - done(new Error("No error thrown")) - }).catch(function(err) { - done(); - }); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/library/index_spec.js b/test/unit/@node-red/runtime/lib/library/index_spec.js deleted file mode 100644 index 2499124c7..000000000 --- a/test/unit/@node-red/runtime/lib/library/index_spec.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -const should = require("should"); -const sinon = require("sinon"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const library = NR_TEST_UTILS.require("@node-red/runtime/lib/library/index") -const localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local") -const examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples") -const events = NR_TEST_UTILS.require("@node-red/util/lib/events") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - - -describe("runtime/library", function() { - before(function() { - sinon.stub(localLibrary,"getEntry").callsFake(function(type,path) { - return Promise.resolve({ - library: "local", - type:type, - path:path - }) - }); - sinon.stub(localLibrary,"saveEntry").callsFake(function(type, path, meta, body) { - return Promise.resolve({ - library: "local", - type:type, - path:path, - meta:meta, - body:body - }) - }); - sinon.stub(examplesLibrary,"getEntry").callsFake(function(type,path) { - return Promise.resolve({ - library: "_examples_", - type:type, - path:path - }) - }); - }); - after(function() { - localLibrary.getEntry.restore(); - localLibrary.saveEntry.restore(); - examplesLibrary.getEntry.restore(); - }) - describe("register", function() { - // it("throws error for duplicate type", function() { - // library.init({}); - // library.register("unknown","/abc"); - // should(()=>{library.register("unknown","/abc")} ).throw(); - // }) - }) - - describe("getLibraries", function() { - before(function() { - library.init({}); - }); - it('returns the default and examples libraries', function() { - const libs = library.getLibraries(); - libs.should.have.length(2); - libs[0].should.have.property('id', 'local'); - libs[0].should.have.property('label','editor:library.types.local'); - libs[0].should.have.property("user", false); - libs[0].should.have.property('icon', 'font-awesome/fa-hdd-o'); - - libs[1].should.have.property('id', 'examples'); - libs[1].should.have.property('label','editor:library.types.examples'); - libs[1].should.have.property("user", false); - libs[1].should.have.property('icon', 'font-awesome/fa-life-ring'); - libs[1].should.have.property('readOnly', true); - libs[1].should.have.property('types', ['flows']); - }); - - it('returns the libraries from settings', function() { - library.init({ - plugins: { - getPlugin: id => { return { - id: "test-library-plugin", - type: "node-red-library-source", - class: function() {} - } - } - }, - settings: { - editorTheme: { - library: { - sources: [ - {id: "test-plugin-id", type:"test-library-plugin"} - ] - } - } - } - }); - let libs = library.getLibraries(); - libs.should.have.length(2); - - events.emit("registry:plugin-added","test-library-plugin" ) - - libs = library.getLibraries(); - libs.should.have.length(3); - libs[2].should.have.property('id', 'test-plugin-id'); - libs[2].should.have.property("user", false); - }); - }) - - describe("getEntry", function() { - before(function() { - library.init({}); - }); - it('throws error for unregistered type', function() { - should(()=>{library.getEntry("local","unknown","/abc")} ).throw(); - }); - it('throws error for unknown library', function() { - should(()=>{library.getEntry("unknown","unknown","/abc")} ).throw(); - }); - - it('returns a registered non-flow entry', function(done) { - library.register("test-module","test-type"); - library.getEntry("local","test-type","/abc").then(function(result) { - result.should.have.property("library","local") - result.should.have.property("type","test-type") - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - - it ('returns a flow entry', function(done) { - library.getEntry("local","flows","/abc").then(function(result) { - result.should.have.property("library","local") - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - - it ('returns a flow example entry', function(done) { - library.getEntry("examples","flows","/test-module/abc").then(function(result) { - result.should.have.property("library","_examples_") - result.should.have.property("path","/test-module/abc") - done(); - }).catch(done); - }); - }); - - describe("saveEntry", function() { - before(function() { - library.init({}); - }); - it('throws error for unknown library', function() { - should(()=>{library.saveEntry("unknown","unknown","/abc",{id:"meta"},{id:"body"})} ).throw(); - }); - it('throws error for unregistered type', function() { - should(()=>{library.saveEntry("local","unknown","/abc",{id:"meta"},{id:"body"})} ).throw(); - }); - it('throws error for save to readonly library', function() { - should(()=>{library.saveEntry("_examples_","unknown","/abc",{id:"meta"},{id:"body"})} ).throw(); - }); - it('saves a flow entry', function(done) { - library.saveEntry('local','flows','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("path","/abc"); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - it('saves a non-flow entry', function(done) { - library.register("test-module","test-type"); - library.saveEntry('local','test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("type","test-type"); - result.should.have.property("path","/abc"); - result.should.have.property("meta",{id:"meta"}); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - - }); -}); diff --git a/test/unit/@node-red/runtime/lib/library/local_spec.js b/test/unit/@node-red/runtime/lib/library/local_spec.js deleted file mode 100644 index 965e5c87a..000000000 --- a/test/unit/@node-red/runtime/lib/library/local_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - -describe("runtime/library/local", function() { - - describe("getEntry", function() { - before(function() { - localLibrary.init({ - log: mockLog, - storage: { - getLibraryEntry: function(type,path) { - return Promise.resolve({type,path}); - } - } - }); - }); - - it('returns a registered non-flow entry', function(done) { - localLibrary.getEntry("test-type","/abc").then(function(result) { - result.should.have.property("type","test-type") - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - - it ('returns a flow entry', function(done) { - localLibrary.getEntry("flows","/abc").then(function(result) { - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - }); - - describe("saveEntry", function() { - before(function() { - localLibrary.init({ - log: mockLog, - storage: { - saveLibraryEntry: function(type, path, meta, body) { - return Promise.resolve({type,path,meta,body}) - } - } - }); - }); - it('saves a flow entry', function(done) { - localLibrary.saveEntry('flows','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("path","/abc"); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - it('saves a non-flow entry', function(done) { - localLibrary.saveEntry('test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("type","test-type"); - result.should.have.property("path","/abc"); - result.should.have.property("meta",{id:"meta"}); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - - }); -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/Node_spec.js b/test/unit/@node-red/runtime/lib/nodes/Node_spec.js deleted file mode 100644 index 3fe676f1e..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/Node_spec.js +++ /dev/null @@ -1,809 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); -var NR_TEST_UTILS = require("nr-test-utils"); -var RedNode = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var Log = NR_TEST_UTILS.require("@node-red/util").log; -var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); - -describe('Node', function() { - describe('#constructor',function() { - it('is called with an id and a type',function() { - var n = new RedNode({id:'123',type:'abc'}); - n.should.have.property('id','123'); - n.should.have.property('type','abc'); - n.should.not.have.property('name'); - n.wires.should.be.empty(); - }); - - it('is called with an id, a type and a name',function() { - var n = new RedNode({id:'123',type:'abc',name:'barney'}); - n.should.have.property('id','123'); - n.should.have.property('type','abc'); - n.should.have.property('name','barney'); - n.wires.should.be.empty(); - }); - - it('is called with an id, a type and some wires',function() { - var n = new RedNode({id:'123',type:'abc',wires:['123','456']}); - n.should.have.property('id','123'); - n.should.have.property('type','abc'); - n.should.not.have.property('name'); - n.wires.should.have.length(2); - }); - - }); - - describe('#close', function() { - it('emits close event when closed',function(done) { - var n = new RedNode({id:'123',type:'abc'}); - n.on('close',function() { - done(); - }); - n.close(); - }); - - it('returns a promise when provided a callback with a done parameter',function(testdone) { - var n = new RedNode({id:'123',type:'abc'}); - n.on('close',function(done) { - setTimeout(function() { - done(); - },50); - }); - var p = n.close(); - should.exist(p); - p.then(function() { - testdone(); - }); - }); - it('accepts a callback with "removed" and "done" parameters', function(testdone) { - var n = new RedNode({id:'123',type:'abc'}); - var receivedRemoved; - n.on('close',function(removed,done) { - receivedRemoved = removed; - setTimeout(function() { - done(); - },50); - }); - var p = n.close(true); - should.exist(p); - (receivedRemoved).should.be.true(); - p.then(function() { - testdone(); - }); - }) - it('allows multiple close handlers to be registered',function(testdone) { - var n = new RedNode({id:'123',type:'abc'}); - var callbacksClosed = 0; - n.on('close',function(done) { - setTimeout(function() { - callbacksClosed++; - done(); - },50); - }); - n.on('close',function(done) { - setTimeout(function() { - callbacksClosed++; - done(); - },75); - }); - n.on('close',function() { - callbacksClosed++; - }); - var p = n.close(); - should.exist(p); - p.then(function() { - callbacksClosed.should.eql(3); - testdone(); - }).catch(function(e) { - testdone(e); - }); - }); - }); - - - describe('#receive', function() { - it('emits input event when called', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"hello world"}; - n.on('input',function(msg) { - should.deepEqual(msg,message); - done(); - }); - n.receive(message); - }); - - it('writes metric info with undefined msg', function(done){ - var n = new RedNode({id:'123',type:'abc'}); - n.on('input',function(msg) { - (typeof msg).should.not.be.equal("undefined"); - (typeof msg._msgid).should.not.be.equal("undefined"); - done(); - }); - n.receive(); - }); - - it('writes metric info with null msg', function(done){ - var n = new RedNode({id:'123',type:'abc'}); - n.on('input',function(msg) { - (typeof msg).should.not.be.equal("undefined"); - (typeof msg._msgid).should.not.be.equal("undefined"); - done(); - }); - n.receive(null); - }); - - it('handles thrown errors', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - sinon.stub(n,"error").callsFake(function(err,msg) {}); - var message = {payload:"hello world"}; - n.on('input',function(msg) { - throw new Error("test error"); - }); - n.receive(message); - setTimeout(function() { - n.error.called.should.be.true(); - n.error.firstCall.args[1].should.equal(message); - done(); - },50); - }); - - it('calls parent flow handleComplete when callback provided', function(done) { - var n = new RedNode({id:'123',type:'abc', _flow: { - handleComplete: function(node,msg) { - try { - msg.should.deepEqual(message); - done(); - } catch(err) { - done(err); - } - } - }}); - - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(); - }); - n.receive(message); - }); - - it('triggers onComplete hook when done callback provided', function(done) { - var handleCompleteCalled = false; - var hookCalled = false; - var n = new RedNode({id:'123',type:'abc', _flow: { - handleComplete: function(node,msg) { - handleCompleteCalled = true; - } - }}); - var hookError; - hooks.add("onComplete",function(completeEvent) { - hookCalled = true; - try { - handleCompleteCalled.should.be.false("onComplete should be called before handleComplete") - should.not.exist(completeEvent.error); - completeEvent.msg.should.deepEqual(message); - completeEvent.node.id.should.eql("123"); - completeEvent.node.node.should.equal(n); - } catch(err) { - hookError = err; - } - }) - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(); - }); - n.receive(message); - setTimeout(function() { - if (hookError) { - done(hookError); - return - } - try { - hookCalled.should.be.true("onComplete hook should be called"); - handleCompleteCalled.should.be.true("handleComplete should be called"); - done(); - } catch(err) { - done(err); - } - }) - }); - - it('triggers onComplete hook when done callback provided - with error', function(done) { - var handleCompleteCalled = false; - var hookCalled = false; - var errorReported = false; - var n = new RedNode({id:'123',type:'abc', _flow: { - handleComplete: function(node,msg) { - handleCompleteCalled = true; - } - }}); - var hookError; - hooks.add("onComplete",function(completeEvent) { - hookCalled = true; - try { - handleCompleteCalled.should.be.false("onComplete should be called before handleComplete") - should.exist(completeEvent.error); - completeEvent.error.toString().should.equal("Error: test error") - completeEvent.msg.should.deepEqual(message); - completeEvent.node.id.should.eql("123"); - completeEvent.node.node.should.equal(n); - } catch(err) { - hookError = err; - } - }) - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(new Error("test error")); - }); - n.error = function(err,msg) { - errorReported = true; - } - n.receive(message); - setTimeout(function() { - if (hookError) { - done(hookError); - return - } - try { - hookCalled.should.be.true("onComplete hook should be called"); - handleCompleteCalled.should.be.false("handleComplete should not be called"); - done(); - } catch(err) { - done(err); - } - }) - }); - it('logs error if callback provides error', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - sinon.stub(n,"error").callsFake(function(err,msg) {}); - - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(new Error("test error")); - setTimeout(function() { - try { - n.error.called.should.be.true(); - n.error.firstCall.args[0].toString().should.equal("Error: test error"); - n.error.firstCall.args[1].should.equal(message); - done(); - } catch(err) { - done(err); - } - },50); - }); - n.receive(message); - }); - it("triggers hooks when receiving a message", function(done) { - var hookErrors = []; - var messageReceived = false; - var hooksCalled = []; - hooks.add("onReceive", function(receiveEvent) { - hooksCalled.push("onReceive") - try { - messageReceived.should.be.false("Message should not have been received before onReceive") - receiveEvent.msg.should.be.exactly(message); - receiveEvent.destination.id.should.equal("123") - receiveEvent.destination.node.should.equal(n) - } catch(err) { - hookErrors.push(err); - } - }) - hooks.add("postReceive", function(receiveEvent) { - hooksCalled.push("postReceive") - try { - messageReceived.should.be.true("Message should have been received before postReceive") - receiveEvent.msg.should.be.exactly(message); - receiveEvent.destination.id.should.equal("123") - receiveEvent.destination.node.should.equal(n) - } catch(err) { - hookErrors.push(err); - } - - }) - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"hello world"}; - n.on('input',function(msg) { - messageReceived = true; - try { - should.strictEqual(this,n); - hooksCalled.should.eql(["onReceive"]) - should.deepEqual(msg,message); - } catch(err) { - hookErrors.push(err) - } - }); - n.receive(message); - setTimeout(function() { - hooks.clear(); - if (hookErrors.length > 0) { - done(hookErrors[0]) - } else { - done(); - } - },10); - }); - describe("errors thrown by hooks are reported", function() { - before(function() { - hooks.add("onReceive",function(recEvent) { - if (recEvent.msg.payload === "trigger-onReceive") { - throw new Error("onReceive Error") - } - }) - hooks.add("postReceive",function(recEvent) { - if (recEvent.msg.payload === "trigger-postReceive") { - throw new Error("postReceive Error") - } - }) - }) - after(function() { - hooks.clear(); - }) - function testHook(hook, msgExpected, done) { - var messageReceived = false; - var errorReceived; - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"trigger-"+hook}; - n.on('input',function(msg) { - messageReceived = true; - }); - n.error = function (err) { - errorReceived = err; - } - - n.receive(message); - - setTimeout(function() { - try { - messageReceived.should.equal(msgExpected,`Hook ${hook} messageReceived expected ${msgExpected} actual ${messageReceived}`); - should.exist(errorReceived); - errorReceived.toString().should.containEql(hook) - done() - } catch(err) { - done(err); - } - },10); - } - it("onReceive", function(done) { testHook("onReceive", false, done)}) - it("postReceive", function(done) { testHook("postReceive", true, done)}) - }) - }); - - describe("hooks can halt receive", function() { - before(function() { - hooks.add("onReceive",function(recEvent) { - if (recEvent.msg.payload === "trigger-onReceive") { - return false; - } - }) - }) - after(function() { - hooks.clear(); - }) - - function testHook(hook, msgExpected, done) { - var messageReceived = false; - var errorReceived; - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"trigger-"+hook}; - n.on('input',function(msg) { - messageReceived = true; - }); - n.error = function (err) { - errorReceived = err; - } - - n.receive(message); - - setTimeout(function() { - try { - messageReceived.should.equal(msgExpected,`Hook ${hook} messageReceived expected ${msgExpected} actual ${messageReceived}`); - should.not.exist(errorReceived); - done() - } catch(err) { - done(err); - } - },10); - } - it("onReceive", function(done) { testHook("onReceive", false, done)}) - }) - - - describe('#send', function() { - - it('emits a single message', function(done) { - var flow = { - send: (sendEvents) => { - try { - sendEvents.should.have.length(1); - sendEvents[0].msg.should.equal(message); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}) - done(); - } catch(err) { - done(err); - } - }, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - var message = {payload:"hello world"}; - n1.send(message); - }); - - it('emits a message with callback provided send', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - handleComplete: (node,msg) => {}, - send: (sendEvents) => { - try { - sendEvents.should.have.length(1); - sendEvents[0].msg.should.equal(message); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[0].cloneMessage.should.be.false(); - done(); - } catch(err) { - done(err); - } - }, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - var message = {payload:"hello world"}; - n1.on('input',function(msg,nodeSend,nodeDone) { - nodeSend(msg); - nodeDone(); - }); - n1.receive(message); - }); - - it('emits multiple messages on a single output', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - send: (sendEvents) => { - try { - sendEvents.should.have.length(2); - sendEvents[0].msg.should.equal(messages[0]); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[0].cloneMessage.should.be.false(); - - sendEvents[1].msg.should.equal(messages[1]); - sendEvents[1].destination.should.eql({id:"n2", node: undefined}); - sendEvents[1].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[1].cloneMessage.should.be.true(); - - done(); - } catch(err) { - done(err); - } - }, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - - var messages = [ - {payload:"hello world"}, - {payload:"hello world again"} - ]; - - n1.send([messages]); - }); - - it('emits messages to multiple outputs', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - send: (sendEvents) => { - try { - sendEvents.should.have.length(3); - sendEvents[0].msg.should.equal(messages[0]); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[0].cloneMessage.should.be.false(); - should.exist(sendEvents[0].msg._msgid); - sendEvents[1].msg.should.equal(messages[2]); - sendEvents[1].destination.should.eql({id:"n4", node: undefined}); - sendEvents[1].source.should.eql({id:"n1", node: n1, port: 2}) - sendEvents[1].cloneMessage.should.be.true(); - should.exist(sendEvents[1].msg._msgid); - sendEvents[2].msg.should.equal(messages[2]); - sendEvents[2].destination.should.eql({id:"n5", node: undefined}); - sendEvents[2].source.should.eql({id:"n1", node: n1, port: 2}) - sendEvents[2].cloneMessage.should.be.true(); - should.exist(sendEvents[2].msg._msgid); - - sendEvents[0].msg._msgid.should.eql(sendEvents[1].msg._msgid) - sendEvents[1].msg._msgid.should.eql(sendEvents[2].msg._msgid) - - done(); - } catch(err) { - done(err); - } - } - }; - var n1 = new RedNode({_flow:flow, id:'n1',type:'abc',wires:[['n2'],['n3'],['n4','n5']]}); - var n2 = new RedNode({_flow:flow, id:'n2',type:'abc'}); - var n3 = new RedNode({_flow:flow, id:'n3',type:'abc'}); - var n4 = new RedNode({_flow:flow, id:'n4',type:'abc'}); - var n5 = new RedNode({_flow:flow, id:'n5',type:'abc'}); - var messages = [ - {payload:"hello world"}, - null, - {payload:"hello world again"} - ]; - - var rcvdCount = 0; - - n1.send(messages); - }); - - it('emits no messages', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - getNode: (id) => { return {'n1':n1,'n2':n2}[id]}, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - var n2 = new RedNode({_flow:flow,id:'n2',type:'abc'}); - - n2.on('input',function(msg) { - should.fail(null,null,"unexpected message"); - }); - - setTimeout(function() { - done(); - }, 200); - - n1.send(); - }); - - // it('emits messages without cloning req or res', function(done) { - // var flow = { - // getNode: (id) => { return {'n1':n1,'n2':n2,'n3':n3}[id]}, - // send: (node,dst,msg) => { setImmediate(function() { flow.getNode(dst).receive(msg) })} - // }; - // var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[[['n2'],['n3']]]}); - // var n2 = new RedNode({_flow:flow,id:'n2',type:'abc'}); - // var n3 = new RedNode({_flow:flow,id:'n3',type:'abc'}); - // - // var req = {}; - // var res = {}; - // var cloned = {}; - // var message = {payload: "foo", cloned: cloned, req: req, res: res}; - // - // var rcvdCount = 0; - // - // // first message to be sent, so should not be cloned - // n2.on('input',function(msg) { - // should.deepEqual(msg, message); - // msg.cloned.should.be.exactly(message.cloned); - // msg.req.should.be.exactly(message.req); - // msg.res.should.be.exactly(message.res); - // rcvdCount += 1; - // if (rcvdCount == 2) { - // done(); - // } - // }); - // - // // second message to be sent, so should be cloned - // // message uuids wont match since we've cloned - // n3.on('input',function(msg) { - // msg.payload.should.equal(message.payload); - // msg.cloned.should.not.be.exactly(message.cloned); - // msg.req.should.be.exactly(message.req); - // msg.res.should.be.exactly(message.res); - // rcvdCount += 1; - // if (rcvdCount == 2) { - // done(); - // } - // }); - // - // n1.send(message); - // }); - - // it("logs the uuid for all messages sent", function(done) { - // var logHandler = { - // msgIds:[], - // messagesSent: 0, - // emit: function(event, msg) { - // if (msg.event == "node.abc.send" && msg.level == Log.METRIC) { - // this.messagesSent++; - // this.msgIds.push(msg.msgid); - // (typeof msg.msgid).should.not.be.equal("undefined"); - // } - // } - // }; - // - // Log.addHandler(logHandler); - // var flow = { - // getNode: (id) => { return {'n1':sender,'n2':receiver1,'n3':receiver2}[id]}, - // send: (node,dst,msg) => { setImmediate(function() { flow.getNode(dst).receive(msg) })} - // }; - // - // var sender = new RedNode({_flow:flow,id:'n1',type:'abc', wires:[['n2', 'n3']]}); - // var receiver1 = new RedNode({_flow:flow,id:'n2',type:'abc'}); - // var receiver2 = new RedNode({_flow:flow,id:'n3',type:'abc'}); - // sender.send({"some": "message"}); - // setTimeout(function() { - // try { - // logHandler.messagesSent.should.equal(1); - // should.exist(logHandler.msgIds[0]) - // Log.removeHandler(logHandler); - // done(); - // } catch(err) { - // Log.removeHandler(logHandler); - // done(err); - // } - // },50) - // }) - }); - - - describe('#log', function() { - it('produces a log message', function(done) { - var n = new RedNode({id:'123',type:'abc',z:'789', _flow: {log:function(msg) { loginfo = msg;}}}); - var loginfo = {}; - n.log("a log message"); - should.deepEqual({level:Log.INFO, id:n.id, - type:n.type, msg:"a log message",z:'789'}, loginfo); - done(); - }); - it('produces a log message with a name', function(done) { - var n = new RedNode({id:'123', type:'abc', name:"barney", z:'789', _flow: {log:function(msg) { loginfo = msg;}}}); - var loginfo = {}; - n.log("a log message"); - should.deepEqual({level:Log.INFO, id:n.id, name: "barney", - type:n.type, msg:"a log message",z:'789'}, loginfo); - done(); - }); - }); - - describe('#warn', function() { - it('produces a warning message', function(done) { - var n = new RedNode({id:'123',type:'abc',z:'789', _flow: {log:function(msg) { loginfo = msg;}}}); - var loginfo = {}; - n.warn("a warning"); - should.deepEqual({level:Log.WARN, id:n.id, - type:n.type, msg:"a warning",z:'789'}, loginfo); - done(); - }); - }); - - describe('#error', function() { - it('handles a null error message', function(done) { - var flow = { - handleError: sinon.stub(), - log:sinon.stub() - } - var n = new RedNode({_flow:flow, id:'123',type:'abc',z:'789'}); - var message = {a:1}; - n.error(null,message); - - flow.handleError.called.should.be.true(); - flow.handleError.args[0][0].should.eql(n); - flow.handleError.args[0][1].should.eql(""); - flow.handleError.args[0][2].should.eql(message); - - done(); - }); - - it('produces an error message', function(done) { - var flow = { - handleError: sinon.stub(), - log:sinon.stub() - } - var n = new RedNode({_flow:flow, id:'123',type:'abc',z:'789'}); - var message = {a:2}; - - n.error("This is an error",message); - - flow.handleError.called.should.be.true(); - flow.handleError.args[0][0].should.eql(n); - flow.handleError.args[0][1].should.eql("This is an error"); - flow.handleError.args[0][2].should.eql(message); - - done(); - }); - - }); - - describe('#metric', function() { - it('produces a metric message', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var loginfo = {}; - sinon.stub(Log, 'log').callsFake(function(msg) { - loginfo = msg; - }); - var msg = {payload:"foo", _msgid:"987654321"}; - n.metric("test.metric",msg,"15mb"); - should.deepEqual({value:"15mb", level:Log.METRIC, nodeid:n.id, - event:"node.abc.test.metric",msgid:"987654321"}, loginfo); - Log.log.restore(); - done(); - }); - }); - - describe('#metric', function() { - it('returns metric value if eventname undefined', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var loginfo = {}; - sinon.stub(Log, 'log').callsFake(function(msg) { - loginfo = msg; - }); - var msg = {payload:"foo", _msgid:"987654321"}; - var m = n.metric(undefined,msg,"15mb"); - m.should.be.a.Boolean(); - Log.log.restore(); - done(); - }); - it('returns not defined if eventname defined', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var loginfo = {}; - sinon.stub(Log, 'log').callsFake(function(msg) { - loginfo = msg; - }); - var msg = {payload:"foo", _msgid:"987654321"}; - var m = n.metric("info",msg,"15mb"); - should(m).be.undefined; - Log.log.restore(); - done(); - }); - }); - - describe('#status', function() { - it('publishes status', function(done) { - var flow = { - handleStatus: sinon.stub() - } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - var status = {fill:"green",shape:"dot",text:"connected"}; - - n.status(status); - - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql(status); - done(); - }); - it('publishes status for plain string', function(done) { - var flow = { handleStatus: sinon.stub() } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - n.status("text status"); - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql({text:"text status"}); - done(); - }); - it('publishes status for plain boolean', function(done) { - var flow = { handleStatus: sinon.stub() } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - n.status(false); - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql({text:"false"}); - done(); - }); - it('publishes status for plain number', function(done) { - var flow = { handleStatus: sinon.stub() } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - n.status(123); - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql({text:"123"}); - done(); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js deleted file mode 100644 index 7e05eba8f..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js +++ /dev/null @@ -1,1209 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); -var path = require("path"); -var fs = require('fs-extra'); -var NR_TEST_UTILS = require("nr-test-utils"); -var Context = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/context/index"); - -describe('context', function() { - describe('local memory',function() { - beforeEach(function() { - Context.init({}); - Context.load(); - }); - afterEach(function() { - Context.clean({allNodes:{}}); - return Context.close(); - }); - it('stores local property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - should.not.exist(context1.get("foo")); - context1.set("foo","test"); - context1.get("foo").should.equal("test"); - }); - it('stores local property - creates parent properties',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - context1.set("foo.bar","test"); - context1.get("foo").should.eql({bar:"test"}); - }); - it('deletes local property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - context1.set("foo.abc.bar1","test1"); - context1.set("foo.abc.bar2","test2"); - context1.get("foo.abc").should.eql({bar1:"test1",bar2:"test2"}); - context1.set("foo.abc.bar1",undefined); - context1.get("foo.abc").should.eql({bar2:"test2"}); - context1.set("foo.abc",undefined); - should.not.exist(context1.get("foo.abc")); - context1.set("foo",undefined); - should.not.exist(context1.get("foo")); - }); - it('stores flow property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - should.not.exist(context1.flow.get("foo")); - context1.flow.set("foo","test"); - context1.flow.get("foo").should.equal("test"); - }); - it('stores global property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - should.not.exist(context1.global.get("foo")); - context1.global.set("foo","test"); - context1.global.get("foo").should.equal("test"); - }); - - it('keeps local context local', function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowA"); - - should.not.exist(context1.get("foo")); - should.not.exist(context2.get("foo")); - context1.set("foo","test"); - - context1.get("foo").should.equal("test"); - should.not.exist(context2.get("foo")); - }); - it('flow context accessible to all flow nodes', function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowA"); - - should.not.exist(context1.flow.get("foo")); - should.not.exist(context2.flow.get("foo")); - - context1.flow.set("foo","test"); - context1.flow.get("foo").should.equal("test"); - context2.flow.get("foo").should.equal("test"); - }); - - it('flow context not shared to nodes on other flows', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB") - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowB"); - - should.not.exist(context1.flow.get("foo")); - should.not.exist(context2.flow.get("foo")); - - context1.flow.set("foo","test"); - context1.flow.get("foo").should.equal("test"); - should.not.exist(context2.flow.get("foo")); - }); - - it('global context shared to all nodes', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB") - - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowB"); - - should.not.exist(context1.global.get("foo")); - should.not.exist(context2.global.get("foo")); - - context1.global.set("foo","test"); - context1.global.get("foo").should.equal("test"); - context2.global.get("foo").should.equal("test"); - }); - - it('context.flow/global are not enumerable', function() { - var flowContextA = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - Object.keys(context1).length.should.equal(0); - Object.keys(context1.flow).length.should.equal(0); - Object.keys(context1.global).length.should.equal(0); - }) - - it('context.flow/global cannot be deleted', function() { - var flowContextA = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - delete context1.flow; - should.exist(context1.flow); - delete context1.global; - should.exist(context1.global); - }) - - it('deletes context',function() { - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - should.not.exist(context.get("foo")); - context.set("foo","abc"); - context.get("foo").should.equal("abc"); - - return Context.delete("1","flowA").then(function(){ - context = Context.get("1","flowA"); - should.not.exist(context.get("foo")); - }); - }); - - it('enumerates context keys - sync', function() { - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - - var keys = context.keys(); - keys.should.be.an.Array(); - keys.should.be.empty(); - - context.set("foo","bar"); - keys = context.keys(); - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("abc.def","bar"); - keys = context.keys(); - keys.should.have.length(2); - keys[1].should.equal("abc"); - }); - - it('enumerates context keys - async', function(done) { - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - - var keys = context.keys(function(err,keys) { - keys.should.be.an.Array(); - keys.should.be.empty(); - context.set("foo","bar"); - keys = context.keys(function(err,keys) { - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("abc.def","bar"); - keys = context.keys(function(err,keys) { - keys.should.have.length(2); - keys[1].should.equal("abc"); - done(); - }); - }); - }); - }); - - it('should enumerate only context keys when GlobalContext was given - sync', function() { - Context.init({functionGlobalContext: {foo:"bar"}}); - Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - context.global.set("foo2","bar2"); - var keys = context.global.keys(); - keys.should.have.length(2); - keys[0].should.equal("foo"); - keys[1].should.equal("foo2"); - }); - }); - - it('should enumerate only context keys when GlobalContext was given - async', function(done) { - Context.init({functionGlobalContext: {foo:"bar"}}); - Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - context.global.set("foo2","bar2"); - context.global.keys(function(err,keys) { - keys.should.have.length(2); - keys[0].should.equal("foo"); - keys[1].should.equal("foo2"); - done(); - }); - }).catch(done); - }); - - - it('returns functionGlobalContext value if store value undefined', function() { - Context.init({functionGlobalContext: {foo:"bar"}}); - return Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - var v = context.global.get('foo'); - v.should.equal('bar'); - }); - }) - - it('returns functionGlobalContext sub-value if store value undefined', function() { - Context.init({functionGlobalContext: {foo:{bar:123}}}); - return Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - var v = context.global.get('foo.bar'); - should.equal(v,123); - }); - }) - - describe("$parent", function() { - it('should get undefined for $parent without key', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - var parent = context1.get("$parent"); - should.equal(parent, undefined); - }); - - it('should get undefined for $parent of root', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - var parent = context1.flow.get("$parent.$parent.K"); - should.equal(parent, undefined); - }); - - it('should get undefined for $parent of root - callback', function(done) { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - context1.flow.get("$parent.$parent.K", function(err, result) { - try { - should.equal(err, undefined); - should.equal(result, undefined); - done(); - } catch(err) { - done(err); - } - }); - - }); - - it('should get value in $parent', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - flowContextA.set("K", "v"); - var v = context1.flow.get("$parent.K"); - should.equal(v, "v"); - }); - - it('should set value in $parent', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - context1.flow.set("$parent.K", "v"); - var v = flowContextA.get("K"); - should.equal(v, "v"); - }); - - it('should not contain $parent in keys', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - var parent = context1.get("$parent"); - flowContextA.set("K0", "v0"); - context1.set("K1", "v1"); - var keys = context1.keys(); - keys.should.have.length(1); - keys[0].should.equal("K1"); - }); - }); - - }); - - describe('external context storage',function() { - var resourcesDir = path.resolve(path.join(__dirname,"..","resources","context")); - var sandbox = sinon.createSandbox(); - var stubGet = sandbox.stub(); - var stubSet = sandbox.stub(); - var stubKeys = sandbox.stub(); - var stubDelete = sandbox.stub().returns(Promise.resolve()); - var stubClean = sandbox.stub().returns(Promise.resolve()); - var stubOpen = sandbox.stub().returns(Promise.resolve()); - var stubClose = sandbox.stub().returns(Promise.resolve()); - var stubGet2 = sandbox.stub(); - var stubSet2 = sandbox.stub(); - var stubKeys2 = sandbox.stub(); - var stubDelete2 = sandbox.stub().returns(Promise.resolve()); - var stubClean2 = sandbox.stub().returns(Promise.resolve()); - var stubOpen2 = sandbox.stub().returns(Promise.resolve()); - var stubClose2 = sandbox.stub().returns(Promise.resolve()); - var testPlugin = function(config){ - function Test(){} - Test.prototype.get = stubGet; - Test.prototype.set = stubSet; - Test.prototype.keys = stubKeys; - Test.prototype.delete = stubDelete; - Test.prototype.clean = stubClean; - Test.prototype.open = stubOpen; - Test.prototype.close = stubClose; - return new Test(config); - }; - var testPlugin2 = function(config){ - function Test2(){} - Test2.prototype.get = stubGet2; - Test2.prototype.set = stubSet2; - Test2.prototype.keys = stubKeys2; - Test2.prototype.delete = stubDelete2; - Test2.prototype.clean = stubClean2; - Test2.prototype.open = stubOpen2; - Test2.prototype.close = stubClose2; - return new Test2(config); - }; - var contextStorage={ - test:{ - module: testPlugin, - config:{} - } - }; - var contextDefaultStorage={ - default: { - module: testPlugin2, - config:{} - }, - test:{ - module: testPlugin, - config:{} - } - }; - var contextAlias={ - default: "test", - test:{ - module: testPlugin, - config:{} - } - }; - var memoryStorage ={ - memory:{ - module: "memory" - } - }; - - afterEach(function() { - sandbox.reset(); - return Context.clean({allNodes:{}}).then(function(){ - return Context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - describe('load modules',function(){ - it('should call open()', function() { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - stubOpen.called.should.be.true(); - stubOpen2.called.should.be.true(); - }); - }); - it('should load memory module', function() { - Context.init({contextStorage:{memory:{module:"memory"}}}); - return Context.load(); - }); - it('should load localfilesystem module', function() { - Context.init({contextStorage:{file:{module:"localfilesystem",config:{dir:resourcesDir}}}}); - return Context.load(); - }); - it('should ignore reserved storage name `_`', function(done) { - Context.init({contextStorage:{_:{module:testPlugin}}}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow") - var context = Context.get("1","flow"); - var cb = function(){} - context.set("foo","bar","_",cb); - context.get("foo","_",cb); - context.keys("_",cb); - stubSet.called.should.be.false(); - stubGet.called.should.be.false(); - stubKeys.called.should.be.false(); - done(); - }).catch(done); - }); - - it('should fail when using invalid store name', function(done) { - Context.init({contextStorage:{'Invalid name':{module:testPlugin}}}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail when using invalid sign character', function (done) { - Context.init({ contextStorage:{'abc-123':{module:testPlugin}}}); - Context.load().then(function () { - done("An error was not thrown"); - }).catch(function () { - done(); - }); - }); - it('should fail when using invalid default context', function(done) { - Context.init({contextStorage:{default:"noexist"}}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail for the storage with no module', function(done) { - Context.init({ contextStorage: { test: {}}}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail to load non-existent module', function(done) { - Context.init({contextStorage:{ file:{module:"nonexistent"} }}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail to load invalid module', function (done) { - Context.init({contextStorage: { - test: { - module: function (config) { - throw new Error("invalid plugin was loaded."); - } - } - }}); - Context.load().then(function () { - done("An error was not thrown"); - }).catch(function () { - done(); - }); - }); - }); - - describe('close modules',function(){ - it('should call close()', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - return Context.close().then(function(){ - stubClose.called.should.be.true(); - stubClose2.called.should.be.true(); - done(); - }); - }).catch(done); - }); - }); - - describe('store context',function() { - it('should store local property to external context storage',function(done) { - Context.init({contextStorage:contextStorage}); - var cb = function(){done("An error occurred")} - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set("foo","bar","test",cb); - context.get("foo","test",cb); - context.keys("test",cb); - stubSet.calledWithExactly("1:flow","foo","bar",cb).should.be.true(); - stubGet.calledWith("1:flow","foo").should.be.true(); - stubKeys.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should store flow property to external context storage',function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.flow.set("foo","bar","test",cb); - context.flow.get("foo","test",cb); - context.flow.keys("test",cb); - stubSet.calledWithExactly("flow","foo","bar",cb).should.be.true(); - stubGet.calledWith("flow","foo").should.be.true(); - stubKeys.calledWithExactly("flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should store global property to external context storage',function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.global.set("foo","bar","test",cb); - context.global.get("foo","test",cb); - context.global.keys("test",cb); - stubSet.calledWithExactly("global","foo","bar",cb).should.be.true(); - stubGet.calledWith("global","foo").should.be.true(); - stubKeys.calledWith("global").should.be.true(); - done(); - }).catch(done); - }); - it('should store data to the default context when non-existent context storage was specified', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","bar","nonexist",cb); - context.get("foo","nonexist",cb); - context.keys("nonexist",cb); - stubGet.called.should.be.false(); - stubSet.called.should.be.false(); - stubKeys.called.should.be.false(); - stubSet2.calledWithExactly("1:flow","foo","bar",cb).should.be.true(); - stubGet2.calledWith("1:flow","foo").should.be.true(); - stubKeys2.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should use the default context', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","bar","default",cb); - context.get("foo","default",cb); - context.keys("default",cb); - stubGet.called.should.be.false(); - stubSet.called.should.be.false(); - stubKeys.called.should.be.false(); - stubSet2.calledWithExactly("1:flow","foo","bar",cb).should.be.true(); - stubGet2.calledWith("1:flow","foo").should.be.true(); - stubKeys2.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should use the alias of default context', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","alias",cb); - context.get("foo",cb); - context.keys(cb); - stubGet.called.should.be.false(); - stubSet.called.should.be.false(); - stubKeys.called.should.be.false(); - stubSet2.calledWithExactly("1:flow","foo","alias",cb).should.be.true(); - stubGet2.calledWith("1:flow","foo").should.be.true(); - stubKeys2.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - - it('should allow the store name to be provide in the key', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("#:(test)::foo","bar"); - context.get("#:(test)::foo"); - stubGet2.called.should.be.false(); - stubSet2.called.should.be.false(); - stubSet.calledWithExactly("1:flow","foo","bar",undefined).should.be.true(); - stubGet.calledWith("1:flow","foo").should.be.true(); - done(); - }).catch(done); - }); - - - it('should use default as the alias of other context', function(done) { - Context.init({contextStorage:contextAlias}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","alias",cb); - context.get("foo",cb); - context.keys(cb); - stubSet.calledWithExactly("1:flow","foo","alias",cb).should.be.true(); - stubGet.calledWith("1:flow","foo").should.be.true(); - stubKeys.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should not throw an error using undefined storage for local context', function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.get("local","nonexist",cb); - done() - }).catch(done); - }); - it('should throw an error using undefined storage for flow context', function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.flow.get("flow","nonexist",cb); - done(); - }).catch(done); - }); - - it('should return functionGlobalContext value as a default - synchronous', function(done) { - var fGC = { "foo": 456 }; - Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC }); - Context.load().then(function() { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - // Get foo - should be value from fGC - var v = context.global.get("foo"); - v.should.equal(456); - - // Update foo - should not touch fGC object - context.global.set("foo","new value"); - fGC.foo.should.equal(456); - - // Get foo - should be the updated value - v = context.global.get("foo"); - v.should.equal("new value"); - done(); - }).catch(done); - }) - - it('should return functionGlobalContext value as a default - async', function(done) { - var fGC = { "foo": 456 }; - Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC }); - Context.load().then(function() { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - // Get foo - should be value from fGC - context.global.get("foo", function(err, v) { - if (err) { - done(err) - } else { - v.should.equal(456); - // Update foo - should not touch fGC object - context.global.set("foo","new value", function(err) { - if (err) { - done(err) - } else { - fGC.foo.should.equal(456); - // Get foo - should be the updated value - context.global.get("foo", function(err, v) { - if (err) { - done(err) - } else { - v.should.equal("new value"); - done(); - } - }); - } - }); - } - }); - }).catch(done); - }) - - it('should return multiple values if key is an array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set("foo1","bar1","memory"); - context.set("foo2","bar2","memory"); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - should.not.exist(foo3); - done(); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should return multiple functionGlobalContext values if key is an array', function(done) { - var fGC = { "foo1": 456, "foo2": {"bar":789} }; - Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC }); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.global.get(["foo1","foo2.bar","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - should.equal(foo1, 456); - should.equal(foo2, 789); - should.not.exist(foo3); - done(); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should return an error if an error occurs in getting multiple store values', function(done) { - Context.init({contextStorage:contextStorage}); - stubGet.onFirstCall().callsArgWith(2, "error2", "bar1"); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow") - var context = Context.get("1","flow"); - context.global.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err === "error2") { - done(); - } else { - done("An error occurred"); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should return a first error if some errors occur in getting multiple store values', function(done) { - Context.init({contextStorage:contextStorage}); - stubGet.onFirstCall().callsArgWith(2, "error1"); - stubGet.onSecondCall().callsArgWith(2, null, "bar2"); - stubGet.onThirdCall().callsArgWith(2, "error3"); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err === "error1") { - done(); - } else { - done("An error occurred"); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should store multiple properties if key and value are arrays', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){ - if (err) { - done(err); - } else { - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - foo3.should.be.equal("bar3"); - done(); - } - }); - } - }); - }); - }); - - it('should deletes multiple properties', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){ - if (err) { - done(err); - } else { - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - foo3.should.be.equal("bar3"); - context.set(["foo1","foo2","foo3"], new Array(3), "memory", function(err){ - if (err) { - done(err); - } else { - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - should.not.exist(foo1); - should.not.exist(foo2); - should.not.exist(foo3); - done(); - } - }); - } - }); - } - }); - } - }); - }); - }); - - it('should use null for missing values if the value array is shorter than the key array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2"], "memory", function(err){ - if (err) { - done(err); - } else { - context.keys(function(err, keys){ - keys.should.have.length(3); - keys.should.eql(["foo1","foo2","foo3"]); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - should(foo3).be.null(); - done(); - } - }); - }); - } - }); - }); - }); - - it('should use null for missing values if the value is not array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], "bar1", "memory", function(err){ - if (err) { - done(err); - } else { - context.keys(function(err, keys){ - keys.should.have.length(3); - keys.should.eql(["foo1","foo2","foo3"]); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - should(foo2).be.null(); - should(foo3).be.null(); - done(); - } - }); - }); - } - }); - }); - }); - - it('should ignore the extra values if the value array is longer than the key array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3","ignored"], "memory", function(err){ - if (err) { - done(err); - } else { - context.keys(function(err, keys){ - keys.should.have.length(3); - keys.should.eql(["foo1","foo2","foo3"]); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - foo3.should.be.equal("bar3"); - done(); - } - }); - }); - } - }); - }); - }); - - it('should return an error if an error occurs in storing multiple values', function(done) { - Context.init({contextStorage:contextStorage}); - stubSet.onFirstCall().callsArgWith(3, "error2"); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){ - if (err === "error2") { - done(); - } else { - done("An error occurred"); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should throw an error if callback of context.get is not a function', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.get("foo", "memory", "callback"); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should not throw an error if callback of context.get is not specified', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.get("foo", "memory"); - done(); - }).catch(done); - }); - - it('should throw an error if callback of context.set is not a function', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.set("foo", "bar", "memory", "callback"); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should not throw an error if callback of context.set is not specified', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.set("foo", "bar", "memory"); - done(); - }).catch(done); - }); - - it('should throw an error if callback of context.keys is not a function', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.keys("memory", "callback"); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should not throw an error if callback of context.keys is not specified', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.keys("memory"); - done(); - }).catch(done); - }); - it('should throw an error in context.get if key is empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(""); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key is an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get({}); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key is a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key array contains an empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(["ok1", "", "ok2"]); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key array contains an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(["ok1", {}, "ok2"]); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key array contains a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(["ok1", 1, "ok2"]); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should throw an error in context.set if key is empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set("", 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key is an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set({}, 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key is a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(1, 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key array contains an empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(["ok1", "", "ok2"], 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key array contains an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(["ok1", {}, "ok2"], 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key array contains a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(["ok1", 1, "ok2"], 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should have an err set in callback for invalid key in context.get', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get("", function(err) { - if(err) { - done(); - } else { - done("should throw an error."); - } - }); - }).catch(done); - }); - - it('should have an err set in callback for invalid key in context.set', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set("", "value", function(err) { - if(err) { - done(); - } else { - done("should throw an error."); - } - }); - }).catch(done); - }); - }); - - describe('listStores', function () { - it('should list context storages', function (done) { - Context.init({ contextStorage: contextDefaultStorage }); - Context.load().then(function () { - var list = Context.listStores(); - list.default.should.equal("default"); - list.stores.should.eql(["default", "test"]); - done(); - }).catch(done); - }); - - it('should list context storages without default storage', function (done) { - Context.init({ contextStorage: contextStorage }); - Context.load().then(function () { - var list = Context.listStores(); - list.default.should.equal("test"); - list.stores.should.eql(["test"]); - done(); - }).catch(done); - }); - }); - describe('delete context',function(){ - it('should not call delete() when external context storage is used', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - Context.get("flowA"); - return Context.delete("flowA").then(function(){ - stubDelete.called.should.be.false(); - stubDelete2.called.should.be.false(); - done(); - }); - }).catch(done); - }); - }); - - describe('clean context',function(){ - it('should call clean()', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - return Context.clean({allNodes:{}}).then(function(){ - stubClean.calledWithExactly([]).should.be.true(); - stubClean2.calledWithExactly([]).should.be.true(); - done(); - }); - }).catch(done); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js deleted file mode 100644 index 26f9789f4..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js +++ /dev/null @@ -1,883 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require('should'); -var fs = require('fs-extra'); -var path = require("path"); -var NR_TEST_UTILS = require("nr-test-utils"); -var LocalFileSystem = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/context/localfilesystem"); - -var resourcesDir = path.resolve(path.join(__dirname,"..","resources","context")); - -var defaultContextBase = "context"; - -describe('localfilesystem',function() { - - before(function() { - return fs.remove(resourcesDir); - }); - - describe('#get/set',function() { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - it('should store property',function(done) { - context.get("nodeX","foo",function(err, value){ - if (err) { return done(err); } - should.not.exist(value); - context.set("nodeX","foo","test",function(err){ - if (err) { return done(err); } - context.get("nodeX","foo",function(err, value){ - if (err) { return done(err); } - value.should.be.equal("test"); - done(); - }); - }); - }); - }); - - it('should store property - creates parent properties',function(done) { - context.set("nodeX","foo.bar","test",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.eql({bar:"test"}); - done(); - }); - }); - }); - - it('should store local scope property', function (done) { - context.set("abc:def", "foo.bar", "test", function (err) { - context.get("abc:def", "foo", function (err, value) { - value.should.be.eql({ bar: "test" }); - done(); - }); - }); - }); - - it('should delete property',function(done) { - context.set("nodeX","foo.abc.bar1","test1",function(err){ - context.set("nodeX","foo.abc.bar2","test2",function(err){ - context.get("nodeX","foo.abc",function(err, value){ - value.should.be.eql({bar1:"test1",bar2:"test2"}); - context.set("nodeX","foo.abc.bar1",undefined,function(err){ - context.get("nodeX","foo.abc",function(err, value){ - value.should.be.eql({bar2:"test2"}); - context.set("nodeX","foo.abc",undefined,function(err){ - context.get("nodeX","foo.abc",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",undefined,function(err){ - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - done(); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }); - - it('should not shared context with other scope', function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.get("nodeY","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","testX",function(err){ - context.set("nodeY","foo","testY",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.equal("testX"); - context.get("nodeY","foo",function(err, value){ - value.should.be.equal("testY"); - done(); - }); - }); - }); - }); - }); - }); - }); - - it('should store string',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","bar",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.String(); - value.should.be.equal("bar"); - context.set("nodeX","foo","1",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.String(); - value.should.be.equal("1"); - done(); - }); - }); - }); - }); - }); - }); - - it('should store number',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",1,function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Number(); - value.should.be.equal(1); - done(); - }); - }); - }); - }); - - it('should store null',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",null,function(err){ - context.get("nodeX","foo",function(err, value){ - should(value).be.null(); - done(); - }); - }); - }); - }); - - it('should store boolean',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",true,function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Boolean().and.true(); - context.set("nodeX","foo",false,function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Boolean().and.false(); - done(); - }); - }); - }); - }); - }); - }); - - it('should store object',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",{obj:"bar"},function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Object(); - value.should.eql({obj:"bar"}); - done(); - }); - }); - }); - }); - - it('should store array',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",["a","b","c"],function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Array(); - value.should.eql(["a","b","c"]); - context.get("nodeX","foo[1]",function(err, value){ - value.should.be.String(); - value.should.equal("b"); - done(); - }); - }); - }); - }); - }); - - it('should store array of arrays',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",[["a","b","c"],[1,2,3,4],[true,false]],function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Array(); - value.should.have.length(3); - value[0].should.have.length(3); - value[1].should.have.length(4); - value[2].should.have.length(2); - context.get("nodeX","foo[1]",function(err, value){ - value.should.be.Array(); - value.should.have.length(4); - value.should.be.eql([1,2,3,4]); - done(); - }); - }); - }); - }); - }); - - it('should store array of objects',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",[{obj:"bar1"},{obj:"bar2"},{obj:"bar3"}],function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Array(); - value.should.have.length(3); - value[0].should.be.Object(); - value[1].should.be.Object(); - value[2].should.be.Object(); - context.get("nodeX","foo[1]",function(err, value){ - value.should.be.Object(); - value.should.be.eql({obj:"bar2"}); - done(); - }); - }); - }); - }); - }); - - it('should set/get multiple values', function(done) { - context.set("nodeX",["one","two","three"],["test1","test2","test3"], function(err) { - context.get("nodeX",["one","two"], function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1","test2"]) - done(); - }); - }); - }) - it('should set/get multiple values - get unknown', function(done) { - context.set("nodeX",["one","two","three"],["test1","test2","test3"], function(err) { - context.get("nodeX",["one","two","unknown"], function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1","test2",undefined]) - done(); - }); - }); - }) - it('should set/get multiple values - single value providd', function(done) { - context.set("nodeX",["one","two","three"],"test1", function(err) { - context.get("nodeX",["one","two"], function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1",null]) - done(); - }); - }); - }) - - it('should throw error if bad key included in multiple keys - get', function(done) { - context.set("nodeX",["one","two","three"],["test1","test2","test3"], function(err) { - context.get("nodeX",["one",".foo","three"], function(err) { - should.exist(err); - done(); - }); - }); - }) - - it('should throw error if bad key included in multiple keys - set', function(done) { - context.set("nodeX",["one",".foo","three"],["test1","test2","test3"], function(err) { - should.exist(err); - // Check 'one' didn't get set as a result - context.get("nodeX","one",function(err,one) { - should.not.exist(one); - done(); - }) - }); - }) - - it('should throw an error when getting a value with invalid key', function (done) { - context.set("nodeX","foo","bar",function(err) { - context.get("nodeX"," ",function(err,value) { - should.exist(err); - done(); - }); - }); - }); - - it('should throw an error when setting a value with invalid key',function (done) { - context.set("nodeX"," ","bar",function (err) { - should.exist(err); - done(); - }); - }); - - it('should throw an error when callback of get() is not a function',function (done) { - try { - context.get("nodeX","foo","callback"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should throw an error when callback of get() is not specified',function (done) { - try { - context.get("nodeX","foo"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should throw an error when callback of set() is not a function',function (done) { - try { - context.set("nodeX","foo","bar","callback"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should not throw an error when callback of set() is not specified', function (done) { - try { - context.set("nodeX"," ","bar"); - done(); - } catch (err) { - done("should not throw an error."); - } - }); - - it('should handle empty context file', function (done) { - fs.outputFile(path.join(resourcesDir,defaultContextBase,"nodeX","flow.json"),"",function(){ - context.get("nodeX", "foo", function (err, value) { - should.not.exist(value); - context.set("nodeX", "foo", "test", function (err) { - context.get("nodeX", "foo", function (err, value) { - value.should.be.equal("test"); - done(); - }); - }); - }); - }); - }); - - it('should throw an error when reading corrupt context file', function (done) { - fs.outputFile(path.join(resourcesDir, defaultContextBase, "nodeX", "flow.json"),"{abc",function(){ - context.get("nodeX", "foo", function (err, value) { - should.exist(err); - done(); - }); - }); - }); - }); - - describe('#keys',function() { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - it('should enumerate context keys', function(done) { - context.keys("nodeX",function(err, value){ - value.should.be.an.Array(); - value.should.be.empty(); - context.set("nodeX","foo","bar",function(err){ - context.keys("nodeX",function(err, value){ - value.should.have.length(1); - value[0].should.equal("foo"); - context.set("nodeX","abc.def","bar",function(err){ - context.keys("nodeX",function(err, value){ - value.should.have.length(2); - value[1].should.equal("abc"); - done(); - }); - }); - }); - }); - }); - }); - - it('should enumerate context keys in each scopes', function(done) { - context.keys("nodeX",function(err, value){ - value.should.be.an.Array(); - value.should.be.empty(); - context.keys("nodeY",function(err, value){ - value.should.be.an.Array(); - value.should.be.empty(); - context.set("nodeX","foo","bar",function(err){ - context.set("nodeY","hoge","piyo",function(err){ - context.keys("nodeX",function(err, value){ - value.should.have.length(1); - value[0].should.equal("foo"); - context.keys("nodeY",function(err, value){ - value.should.have.length(1); - value[0].should.equal("hoge"); - done(); - }); - }); - }); - }); - }); - }); - }); - - it('should throw an error when callback of keys() is not a function', function (done) { - try { - context.keys("nodeX", "callback"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should throw an error when callback of keys() is not specified', function (done) { - try { - context.keys("nodeX"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - }); - - describe('#delete',function() { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - it('should delete context',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.get("nodeY","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","testX",function(err){ - context.set("nodeY","foo","testY",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.equal("testX"); - context.get("nodeY","foo",function(err, value){ - value.should.be.equal("testY"); - context.delete("nodeX").then(function(){ - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.get("nodeY","foo",function(err, value){ - value.should.be.equal("testY"); - done(); - }); - }); - }).catch(done); - }); - }); - }); - }); - }); - }); - }); - }); - - describe('#clean',function() { - var context; - var contextGet; - var contextSet; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - contextGet = function(scope,key) { - return new Promise((res,rej) => { - context.get(scope,key, function(err,value) { - if (err) { - rej(err); - } else { - res(value); - } - }) - }); - } - contextSet = function(scope,key,value) { - return new Promise((res,rej) => { - context.set(scope,key,value, function(err) { - if (err) { - rej(err); - } else { - res(); - } - }) - }); - } - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close().then(function(){ - return fs.remove(resourcesDir); - }); - }); - }); - it('should clean unnecessary context',function(done) { - contextSet("global","foo","testGlobal").then(function() { - return contextSet("nodeX:flow1","foo","testX"); - }).then(function() { - return contextSet("nodeY:flow2","foo","testY"); - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - value.should.be.equal("testX"); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - value.should.be.equal("testY"); - }).then(function() { - return context.clean([]) - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - should.not.exist(value); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - should.not.exist(value); - }).then(function() { - return contextGet("global","foo"); - }).then(function(value) { - value.should.eql("testGlobal"); - }).then(done).catch(done); - }); - - it('should not clean active context',function(done) { - contextSet("global","foo","testGlobal").then(function() { - return contextSet("nodeX:flow1","foo","testX"); - }).then(function() { - return contextSet("nodeY:flow2","foo","testY"); - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - value.should.be.equal("testX"); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - value.should.be.equal("testY"); - }).then(function() { - return context.clean(["flow1","nodeX"]) - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - value.should.be.equal("testX"); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - should.not.exist(value); - }).then(function() { - return contextGet("global","foo"); - }).then(function(value) { - value.should.eql("testGlobal"); - }).then(done).catch(done); - }); - }); - - describe('#if cache is enabled',function() { - - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - - - it('should load contexts into the cache',function() { - var globalData = {key:"global"}; - var flowData = {key:"flow"}; - var nodeData = {key:"node"}; - return Promise.all([ - fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8"), - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flow","flow.json"), JSON.stringify(flowData,null,4), "utf8"), - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flow","node.json"), JSON.stringify(nodeData,null,4), "utf8") - ]).then(function(){ - context = LocalFileSystem({dir: resourcesDir, cache: true}); - return context.open(); - }).then(function(){ - return Promise.all([ - fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")), - fs.remove(path.join(resourcesDir,defaultContextBase,"flow","flow.json")), - fs.remove(path.join(resourcesDir,defaultContextBase,"flow","node.json")) - ]); - }).then(function(){ - context.get("global","key").should.be.equal("global"); - context.get("flow","key").should.be.equal("flow"); - context.get("node:flow","key").should.be.equal("node"); - }); - }); - - it('should store property to the cache',function() { - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 1}); - return context.open().then(function(){ - return new Promise(function(resolve, reject){ - context.set("global","foo","bar",function(err){ - if(err){ - reject(err); - } else { - fs.readJson(path.join(resourcesDir,defaultContextBase,"global","global.json")).then(function(data) { - // File should not exist as flush hasn't happened - reject("File global/global.json should not exist"); - }).catch(function(err) { - setTimeout(function() { - fs.readJson(path.join(resourcesDir,defaultContextBase,"global","global.json")).then(function(data) { - data.should.eql({foo:'bar'}); - resolve(); - }).catch(function(err) { - reject(err); - }); - },1100) - }) - } - }); - }); - }).then(function(){ - return fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")); - }).then(function(){ - context.get("global","foo").should.be.equal("bar"); - }) - }); - - it('should enumerate context keys in the cache',function() { - var globalData = {foo:"bar"}; - return fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8").then(function(){ - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2}); - return context.open() - }).then(function(){ - return fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")); - }).then(function(){ - var keys = context.keys("global"); - keys.should.have.length(1); - keys[0].should.equal("foo"); - return new Promise(function(resolve, reject){ - context.set("global","foo2","bar2",function(err){ - if(err){ - reject(err); - } else { - resolve(); - } - }); - }); - }).then(function(){ - return fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")); - }).then(function(){ - var keys = context.keys("global"); - keys.should.have.length(2); - keys[1].should.equal("foo2"); - }) - }); - - it('should delete context in the cache',function() { - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2}); - return context.open().then(function(){ - return new Promise(function(resolve, reject){ - context.set("global","foo","bar",function(err){ - if(err){ - reject(err); - } else { - resolve(); - } - }); - }); - }).then(function(){ - context.get("global","foo").should.be.equal("bar"); - return context.delete("global"); - }).then(function(){ - should.not.exist(context.get("global","foo")) - }) - }); - - it('should clean unnecessary context in the cache',function() { - var flowAData = {key:"flowA"}; - var flowBData = {key:"flowB"}; - return Promise.all([ - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flowA","flow.json"), JSON.stringify(flowAData,null,4), "utf8"), - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flowB","flow.json"), JSON.stringify(flowBData,null,4), "utf8") - ]).then(function(){ - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2}); - return context.open(); - }).then(function(){ - context.get("flowA","key").should.be.equal("flowA"); - context.get("flowB","key").should.be.equal("flowB"); - return context.clean(["flowA"]); - }).then(function(){ - context.get("flowA","key").should.be.equal("flowA"); - should.not.exist(context.get("flowB","key")); - }); - }); - }); - - describe('Configuration', function () { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - it('should change a base directory', function (done) { - var differentBaseContext = LocalFileSystem({ - base: "contexts2", - dir: resourcesDir, - cache: false - }); - differentBaseContext.open().then(function () { - differentBaseContext.set("node2", "foo2", "bar2", function (err) { - differentBaseContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function(err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - }); - - it('should use userDir', function (done) { - var userDirContext = LocalFileSystem({ - base: "contexts2", - cache: false, - settings: { - userDir: resourcesDir - } - }); - userDirContext.open().then(function () { - userDirContext.set("node2", "foo2", "bar2", function (err) { - userDirContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - }); - - it('should use NODE_RED_HOME', function (done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = resourcesDir; - fs.ensureDirSync(resourcesDir); - fs.writeFileSync(path.join(resourcesDir,".config.json"),""); - var nrHomeContext = LocalFileSystem({ - base: "contexts2", - cache: false - }); - try { - nrHomeContext.open().then(function () { - nrHomeContext.set("node2", "foo2", "bar2", function (err) { - nrHomeContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - } finally { - process.env.NODE_RED_HOME = oldNRH; - } - }); - - it('should use HOME_PATH', function (done) { - var oldNRH = process.env.NODE_RED_HOME; - var oldHOMEPATH = process.env.HOMEPATH; - process.env.NODE_RED_HOME = resourcesDir; - process.env.HOMEPATH = resourcesDir; - var homePath = path.join(resourcesDir, ".node-red"); - fs.outputFile(path.join(homePath, ".config.json"),"",function(){ - var homeContext = LocalFileSystem({ - base: "contexts2", - cache: false - }); - try { - homeContext.open().then(function () { - homeContext.set("node2", "foo2", "bar2", function (err) { - homeContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOMEPATH = oldHOMEPATH; - } - }); - }); - - it('should use HOME_PATH', function (done) { - var oldNRH = process.env.NODE_RED_HOME; - var oldHOMEPATH = process.env.HOMEPATH; - var oldHOME = process.env.HOME; - process.env.NODE_RED_HOME = resourcesDir; - process.env.HOMEPATH = resourcesDir; - process.env.HOME = resourcesDir; - var homeContext = LocalFileSystem({ - base: "contexts2", - cache: false - }); - try { - homeContext.open().then(function () { - homeContext.set("node2", "foo2", "bar2", function (err) { - homeContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOMEPATH = oldHOMEPATH; - process.env.HOME = oldHOME; - } - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js deleted file mode 100644 index 663ee46b7..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require('should'); -var NR_TEST_UTILS = require("nr-test-utils"); - -var Memory = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/context/memory"); - -describe('memory',function() { - var context; - - beforeEach(function() { - context = Memory({}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }); - }); - - describe('#get/set',function() { - describe('sync',function() { - it('should store property',function() { - should.not.exist(context.get("nodeX","foo")); - context.set("nodeX","foo","test"); - context.get("nodeX","foo").should.equal("test"); - }); - - it('should store property - creates parent properties',function() { - context.set("nodeX","foo.bar","test"); - context.get("nodeX","foo").should.eql({bar:"test"}); - }); - - it('should delete property',function() { - context.set("nodeX","foo.abc.bar1","test1"); - context.set("nodeX","foo.abc.bar2","test2"); - context.get("nodeX","foo.abc").should.eql({bar1:"test1",bar2:"test2"}); - context.set("nodeX","foo.abc.bar1",undefined); - context.get("nodeX","foo.abc").should.eql({bar2:"test2"}); - context.set("nodeX","foo.abc",undefined); - should.not.exist(context.get("nodeX","foo.abc")); - context.set("nodeX","foo",undefined); - should.not.exist(context.get("nodeX","foo")); - }); - - it('should not shared context with other scope', function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","testX"); - context.set("nodeY","foo","testY"); - - context.get("nodeX","foo").should.equal("testX"); - context.get("nodeY","foo").should.equal("testY"); - }); - - it('should throw the error if the error occurs', function() { - try{ - context.set("nodeX",".foo","test"); - should.fail("Error was not thrown"); - }catch(err){ - should.exist(err); - try{ - context.get("nodeX",".foo"); - should.fail("Error was not thrown"); - }catch(err){ - should.exist(err); - } - } - }); - - it('should get multiple values - all known', function() { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - var values = context.get("nodeX",["one","two","four"]); - values.should.eql(["test1","test2","test4"]) - }) - it('should get multiple values - include unknown', function() { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - var values = context.get("nodeX",["one","unknown.with.multiple.levels"]); - values.should.eql(["test1",undefined]) - }) - it('should throw error if bad key included in multiple keys', function() { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - try{ - var values = context.get("nodeX",["one",".foo","three"]); - should.fail("Error was not thrown"); - }catch(err){ - should.exist(err); - } - }) - - - }); - - describe('async',function() { - it('should store property',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","test",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.equal("test"); - done(); - }); - }); - }); - }); - - it('should pass the error to callback if the error occurs',function(done) { - context.set("nodeX",".foo","test",function(err, value){ - should.exist(err); - context.get("nodeX",".foo",function(err){ - should.exist(err); - done(); - }); - }); - }); - - it('should get multiple values - all known', function(done) { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - context.get("nodeX",["one","two","four"],function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1","test2","test4"]) - done(); - }); - }) - it('should get multiple values - include unknown', function(done) { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - context.get("nodeX",["one","unknown"],function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1",undefined]) - done(); - }); - }) - it('should throw error if bad key included in multiple keys', function(done) { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - context.get("nodeX",["one",".foo","three"], function(err) { - should.exist(err); - done(); - }); - }) - }); - }); - - describe('#keys',function() { - describe('sync',function() { - it('should enumerate context keys', function() { - var keys = context.keys("nodeX"); - keys.should.be.an.Array(); - keys.should.be.empty(); - - context.set("nodeX","foo","bar"); - keys = context.keys("nodeX"); - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("nodeX","abc.def","bar"); - keys = context.keys("nodeX"); - keys.should.have.length(2); - keys[1].should.equal("abc"); - }); - - it('should enumerate context keys in each scopes', function() { - var keysX = context.keys("nodeX"); - keysX.should.be.an.Array(); - keysX.should.be.empty(); - - var keysY = context.keys("nodeY"); - keysY.should.be.an.Array(); - keysY.should.be.empty(); - - context.set("nodeX","foo","bar"); - context.set("nodeY","hoge","piyo"); - keysX = context.keys("nodeX"); - keysX.should.have.length(1); - keysX[0].should.equal("foo"); - - keysY = context.keys("nodeY"); - keysY.should.have.length(1); - keysY[0].should.equal("hoge"); - }); - - it('should enumerate global context keys', function () { - var keys = context.keys("global"); - keys.should.be.an.Array(); - keys.should.be.empty(); - - context.set("global", "foo", "bar"); - keys = context.keys("global"); - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("global", "abc.def", "bar"); - keys = context.keys("global"); - keys.should.have.length(2); - keys[1].should.equal("abc"); - }); - - it('should not return specific keys as global context keys', function () { - var keys = context.keys("global"); - - context.set("global", "set", "bar"); - context.set("global", "get", "bar"); - context.set("global", "keys", "bar"); - keys = context.keys("global"); - keys.should.have.length(0); - }); - }); - - describe('async',function() { - it('should enumerate context keys', function(done) { - context.keys("nodeX", function(err, keys) { - keys.should.be.an.Array(); - keys.should.be.empty(); - context.set("nodeX", "foo", "bar", function(err) { - context.keys("nodeX", function(err, keys) { - keys.should.have.length(1); - keys[0].should.equal("foo"); - context.set("nodeX","abc.def","bar",function(err){ - context.keys("nodeX",function(err, keys){ - keys.should.have.length(2); - keys[1].should.equal("abc"); - done(); - }); - }); - }); - }); - }); - }); - }); - }); - - describe('#delete',function() { - it('should delete context',function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","abc"); - context.set("nodeY","foo","abc"); - context.get("nodeX","foo").should.equal("abc"); - context.get("nodeY","foo").should.equal("abc"); - - return context.delete("nodeX").then(function(){ - should.not.exist(context.get("nodeX","foo")); - should.exist(context.get("nodeY","foo")); - }); - }); - }); - - describe('#clean',function() { - it('should clean unnecessary context',function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","abc"); - context.set("nodeY","foo","abc"); - context.get("nodeX","foo").should.equal("abc"); - context.get("nodeY","foo").should.equal("abc"); - - return context.clean([]).then(function(){ - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - }); - }); - it('should not clean active context',function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","abc"); - context.set("nodeY","foo","abc"); - context.get("nodeX","foo").should.equal("abc"); - context.get("nodeY","foo").should.equal("abc"); - - return context.clean(["nodeX"]).then(function(){ - should.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - }); - }); - it('should not clean global context', function () { - context.set("global", "foo", "abc"); - context.get("global", "foo").should.equal("abc"); - - return context.clean(["global"]).then(function () { - should.exist(context.get("global", "foo")); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/credentials_spec.js b/test/unit/@node-red/runtime/lib/nodes/credentials_spec.js deleted file mode 100644 index 6db0b867b..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/credentials_spec.js +++ /dev/null @@ -1,474 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var index = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/index"); -var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); -var log = NR_TEST_UTILS.require("@node-red/util").log; - - -describe('red/runtime/nodes/credentials', function() { - - var encryptionDisabledSettings = { - get: function(key) { - return false; - } - } - - afterEach(function() { - index.clearRegistry(); - }); - - it('loads provided credentials',function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - - return credentials.load({"a":{"b":1,"c":2}}).then(function() { - credentials.get("a").should.have.property('b',1); - credentials.get("a").should.have.property('c',2); - }); - }); - it('adds a new credential',function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - return credentials.load({"a":{"b":1,"c":2}}).then(function() { - credentials.dirty().should.be.false(); - should.not.exist(credentials.get("b")); - return credentials.add("b",{"foo":"bar"}).then(function() { - credentials.get("b").should.have.property("foo","bar"); - credentials.dirty().should.be.true(); - }); - }); - }); - it('deletes an existing credential',function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - return credentials.load({"a":{"b":1,"c":2}}).then(function() { - credentials.dirty().should.be.false(); - credentials.delete("a"); - should.not.exist(credentials.get("a")); - credentials.dirty().should.be.true(); - }); - }); - - it('exports the credentials, clearing dirty flag', function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - var creds = {"a":{"b":1,"c":2}}; - return credentials.load(creds).then(function() { - return credentials.add("b",{"foo":"bar"}) - }).then(function() { - credentials.dirty().should.be.true(); - return credentials.export().then(function(exported) { - exported.should.eql(creds); - credentials.dirty().should.be.false(); - }) - }); - }) - - describe("#clean",function() { - it("removes credentials of unknown nodes",function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - var creds = {"a":{"b":1,"c":2},"b":{"d":3}}; - return credentials.load(creds).then(function() { - credentials.dirty().should.be.false(); - should.exist(credentials.get("a")); - should.exist(credentials.get("b")); - return credentials.clean([{id:"b"}]).then(function() { - credentials.dirty().should.be.true(); - should.not.exist(credentials.get("a")); - should.exist(credentials.get("b")); - }); - }); - }); - it("extracts credentials of known nodes",function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - credentials.register("testNode",{"b":"text","c":"password"}) - var creds = {"a":{"b":1,"c":2}}; - var newConfig = [{id:"a",type:"testNode",credentials:{"b":"newBValue","c":"newCValue"}}]; - return credentials.load(creds).then(function() { - credentials.dirty().should.be.false(); - return credentials.clean(newConfig).then(function() { - credentials.dirty().should.be.true(); - credentials.get("a").should.have.property('b',"newBValue"); - credentials.get("a").should.have.property('c',"newCValue"); - should.not.exist(newConfig[0].credentials); - }); - }); - }); - - - }); - - it('warns if a node has no credential definition', function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - return credentials.load({}).then(function() { - var node = {id:"node",type:"test",credentials:{ - user1:"newUser", - password1:"newPassword" - }}; - sinon.spy(log,"warn"); - credentials.extract(node); - log.warn.called.should.be.true(); - should.not.exist(node.credentials); - log.warn.restore(); - }); - }) - - it('extract credential updates in the provided node', function(done) { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - var defintion = { - user1:{type:"text"}, - password1:{type:"password"}, - user2:{type:"text"}, - password2:{type:"password"}, - user3:{type:"text"}, - password3:{type:"password"} - - }; - credentials.register("test",defintion); - var def = credentials.getDefinition("test"); - defintion.should.eql(def); - - credentials.load({"node":{user1:"abc",password1:"123",user2:"def",password2:"456",user3:"ghi",password3:"789"}}).then(function() { - var node = {id:"node",type:"test",credentials:{ - // user1 unchanged - password1:"__PWRD__", - user2: "", - password2:" ", - user3:"newUser", - password3:"newPassword" - }}; - credentials.dirty().should.be.false(); - credentials.extract(node); - - node.should.not.have.a.property("credentials"); - - credentials.dirty().should.be.true(); - var newCreds = credentials.get("node"); - newCreds.should.have.a.property("user1","abc"); - newCreds.should.have.a.property("password1","123"); - newCreds.should.not.have.a.property("user2"); - newCreds.should.not.have.a.property("password2"); - newCreds.should.have.a.property("user3","newUser"); - newCreds.should.have.a.property("password3","newPassword"); - - done(); - }); - }); - it('extract ignores node without credentials', function(done) { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() { - var node = {id:"node",type:"test"}; - - credentials.dirty().should.be.false(); - credentials.extract(node); - credentials.dirty().should.be.false(); - done(); - }); - }); - - describe("encryption",function() { - var settings = {}; - var runtime = { - log: log, - settings: { - get: function(key) { - return settings[key]; - }, - set: function(key,value) { - settings[key] = value; - return Promise.resolve(); - }, - delete: function(key) { - delete settings[key]; - return Promise.resolve(); - } - }, - nodes: { getType: () => function(){} } - } - it('migrates to encrypted and generates default key', function(done) { - settings = {}; - credentials.init(runtime); - credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() { - settings.should.have.a.property("_credentialSecret"); - settings._credentialSecret.should.have.a.length(64); - credentials.dirty().should.be.true(); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - done(); - }) - }); - }); - }); - it('uses default key', function(done) { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - should.exist(credentials.get("node")); - credentials.dirty().should.be.false(); - credentials.add("node",{user1:"def",password1:"456"}); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","def"); - credentials.get("node").should.have.a.property("password1","456"); - done(); - }) - }); - }); - }); - it('uses user key', function(done) { - settings = { - credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - credentials.dirty().should.be.false(); - should.exist(credentials.get("node")); - credentials.add("node",{user1:"def",password1:"456"}); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","def"); - credentials.get("node").should.have.a.property("password1","456"); - done(); - }) - }); - }); - }); - it('uses user key - when settings are otherwise unavailable', function(done) { - var runtime = { - log: log, - settings: { - get: function(key) { - if (key === 'credentialSecret') { - return "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a"; - } - throw new Error(); - }, - set: function(key,value) { - throw new Error(); - } - } - } - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - should.exist(credentials.get("node")); - credentials.add("node",{user1:"def",password1:"456"}); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","def"); - credentials.get("node").should.have.a.property("password1","456"); - done(); - }) - }); - }); - }); - it('migrates from default key to user key', function() { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a", - credentialSecret: "aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbcccccccccccccddddddddddddeeeee" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - return credentials.load(cryptedFlows).then(function() { - credentials.dirty().should.be.true(); - should.exist(credentials.get("node")); - return credentials.export().then(function(result) { - result.should.have.a.property("$"); - settings.should.not.have.a.property("_credentialSecret"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - return credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","abc"); - credentials.get("node").should.have.a.property("password1","123"); - }) - }); - }); - }); - - it('migrates from default key to user key - unencrypted original', function(done) { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a", - credentialSecret: "aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbcccccccccccccddddddddddddeeeee" - }; - // {"node":{user1:"abc",password1:"123"}} - var unencryptedFlows = {"node":{user1:"abc",password1:"123"}}; - credentials.init(runtime); - credentials.load(unencryptedFlows).then(function() { - credentials.dirty().should.be.true(); - should.exist(credentials.get("node")); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - settings.should.not.have.a.property("_credentialSecret"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","abc"); - credentials.get("node").should.have.a.property("password1","123"); - done(); - }) - }); - }); - }); - - it('migrates from default key to unencrypted', function(done) { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a", - credentialSecret: false - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - credentials.dirty().should.be.true(); - should.exist(credentials.get("node")); - credentials.export().then(function(result) { - result.should.not.have.a.property("$"); - settings.should.not.have.a.property("_credentialSecret"); - result.should.eql({"node":{user1:"abc",password1:"123"}}); - done(); - }); - }); - }); - it('handles bad default key - resets credentials', function(done) { - settings = { - _credentialSecret: "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - // credentials.dirty().should.be.true(); - // should.not.exist(credentials.get("node")); - done(); - }).catch(function(err) { - err.should.have.property('code','credentials_load_failed'); - done(); - }); - }); - it('handles bad user key - resets credentials', function(done) { - settings = { - credentialSecret: "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - // credentials.dirty().should.be.true(); - // should.not.exist(credentials.get("node")); - done(); - }).catch(function(err) { - err.should.have.property('code','credentials_load_failed'); - done(); - }); - }); - - it('handles unavailable settings - leaves creds unencrypted', function(done) { - var runtime = { - log: log, - settings: { - get: function(key) { - throw new Error(); - }, - set: function(key,value) { - throw new Error(); - } - }, - nodes: { getType: () => function(){} } - } - // {"node":{user1:"abc",password1:"123"}} - credentials.init(runtime); - credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() { - credentials.dirty().should.be.false(); - should.exist(credentials.get("node")); - credentials.export().then(function(result) { - result.should.not.have.a.property("$"); - result.should.have.a.property("node"); - done(); - }); - }); - }); - }) -}) diff --git a/test/unit/@node-red/runtime/lib/nodes/index_spec.js b/test/unit/@node-red/runtime/lib/nodes/index_spec.js deleted file mode 100644 index d6017db45..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/index_spec.js +++ /dev/null @@ -1,404 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); -var sinon = require('sinon'); -var inherits = require("util").inherits; - -var NR_TEST_UTILS = require("nr-test-utils"); -var index = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/index"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var registry = NR_TEST_UTILS.require("@node-red/registry") -var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); - -describe("red/nodes/index", function() { - before(function() { - sinon.stub(index,"startFlows"); - process.env.NODE_RED_HOME = NR_TEST_UTILS.resolve("node-red"); - process.env.foo="bar"; - }); - after(function() { - index.startFlows.restore(); - delete process.env.NODE_RED_HOME; - delete process.env.foo; - }); - - afterEach(function() { - index.clearRegistry(); - }); - - var testFlows = [{"type":"test","id":"tab1","label":"Sheet 1"}]; - var testCredentials = {"tab1":{"b":1, "c":"2", "d":"$(foo)"}}; - var storage = { - getFlows: function() { - return Promise.resolve({red:123,flows:testFlows,credentials:testCredentials}); - }, - saveFlows: function(conf) { - should.deepEqual(testFlows, conf.flows); - return Promise.resolve(123); - } - }; - - var settings = { - available: function() { return false }, - get: function() { return false } - }; - - var EventEmitter = require('events').EventEmitter; - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:function() {}}, - events: new EventEmitter() - }; - - function TestNode(n) { - this._flow = {getSetting: p => process.env[p]}; - index.createNode(this, n); - this.on("log", function() { - // do nothing - }); - } - - it('nodes are initialised with credentials',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - var testnode = new TestNode({id:'tab1',type:'test',name:'barney'}); - testnode.credentials.should.have.property('b',1); - testnode.credentials.should.have.property('c',"2"); - testnode.credentials.should.have.property('d',"bar"); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('flows should be initialised',function(done) { - index.init(runtime); - index.loadFlows().then(function() { - // console.log(testFlows); - // console.log(index.getFlows()); - should.deepEqual(testFlows, index.getFlows().flows); - done(); - }).catch(function(err) { - done(err); - }); - - }); - describe("registerType", function() { - describe("logs deprecated usage", function() { - before(function() { - sinon.stub(registry,"registerType"); - }); - after(function() { - registry.registerType.restore(); - }); - it("called without node-set name", function() { - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:sinon.spy()}, - events: new EventEmitter() - } - index.init(runtime); - - index.registerType(/*'test-node-set',*/'test', TestNode, {}); - runtime.log.warn.called.should.be.true(); - registry.registerType.called.should.be.true(); - registry.registerType.firstCall.args[0].should.eql(''); - registry.registerType.firstCall.args[1].should.eql('test'); - registry.registerType.firstCall.args[2].should.eql(TestNode); - }); - }); - describe("extends constructor with Node constructor", function() { - var TestNodeConstructor; - before(function() { - sinon.stub(registry,"registerType"); - }); - after(function() { - registry.registerType.restore(); - }); - beforeEach(function() { - TestNodeConstructor = function TestNodeConstructor() {}; - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:sinon.spy()}, - events: new EventEmitter() - } - index.init(runtime); - }) - it('extends a constructor with the Node constructor', function() { - TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node); - index.registerType('node-set','node-type',TestNodeConstructor); - TestNodeConstructor.prototype.should.be.an.instanceOf(Node); - }); - it('does not override a constructor prototype', function() { - function Foo(){}; - inherits(TestNodeConstructor,Foo); - TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); - TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node); - - index.registerType('node-set','node-type',TestNodeConstructor); - - TestNodeConstructor.prototype.should.be.an.instanceOf(Node); - TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); - - index.registerType('node-set','node-type2',TestNodeConstructor); - TestNodeConstructor.prototype.should.be.an.instanceOf(Node); - TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); - }); - }); - describe("register credentials definition", function() { - var http = require('http'); - var express = require('express'); - var app = express(); - var runtime = NR_TEST_UTILS.require("@node-red/runtime"); - var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); - var localfilesystem = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem"); - var log = NR_TEST_UTILS.require("@node-red/util").log; - var RED = NR_TEST_UTILS.require("node-red/lib/red.js"); - - var userDir = path.join(__dirname,".testUserHome"); - before(function(done) { - sinon.stub(log,"log").callsFake(function(){}); - fs.remove(userDir,function(err) { - fs.mkdir(userDir,function() { - sinon.stub(index, 'load').callsFake(function() { - return new Promise(function(resolve,reject){ - resolve([]); - }); - }); - sinon.stub(localfilesystem, 'getCredentials').callsFake(function() { - return new Promise(function(resolve,reject) { - resolve({"tab1":{"b":1,"c":2}}); - }); - }) ; - RED.init(http.createServer(function(req,res){app(req,res)}), - {userDir: userDir}); - runtime.start().then(function () { - done(); - }); - }); - }); - }); - - after(function(done) { - fs.remove(userDir,function() { - runtime.stop().then(function() { - index.load.restore(); - localfilesystem.getCredentials.restore(); - log.log.restore(); - done(); - }); - }); - }); - - it('definition defined',function() { - index.registerType('test-node-set','test', TestNode, { - credentials: { - foo: {type:"test"} - } - }); - var testnode = new TestNode({id:'tab1',type:'test',name:'barney', '_alias':'tab1'}); - index.getCredentialDefinition("test").should.have.property('foo'); - }); - }); - - describe("register settings definition", function() { - beforeEach(function() { - sinon.stub(registry,"registerType"); - }) - afterEach(function() { - registry.registerType.restore(); - }) - it('registers valid settings',function() { - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:function() {}}, - events: new EventEmitter() - } - runtime.settings.registerNodeSettings = sinon.spy(); - index.init(runtime); - - index.registerType('test-node-set','test', TestNode, { - settings: { - testOne: {} - } - }); - runtime.settings.registerNodeSettings.called.should.be.true(); - runtime.settings.registerNodeSettings.firstCall.args[0].should.eql('test'); - runtime.settings.registerNodeSettings.firstCall.args[1].should.eql({testOne: {}}); - }); - it('logs invalid settings',function() { - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:sinon.spy()}, - events: new EventEmitter() - } - runtime.settings.registerNodeSettings = function() { throw new Error("pass");} - index.init(runtime); - - index.registerType('test-node-set','test', TestNode, { - settings: { - testOne: {} - } - }); - runtime.log.warn.called.should.be.true(); - }); - }); - }); - - describe('allows nodes to be added/removed/enabled/disabled from the registry', function() { - var randomNodeInfo = {id:"5678",types:["random"]}; - - beforeEach(function() { - sinon.stub(registry,"getNodeInfo").callsFake(function(id) { - if (id == "test") { - return {id:"1234",types:["test"]}; - } else if (id == "doesnotexist") { - return null; - } else { - return randomNodeInfo; - } - }); - sinon.stub(registry,"disableNode").callsFake(function(id) { - return Promise.resolve(randomNodeInfo); - }); - }); - afterEach(function() { - registry.getNodeInfo.restore(); - registry.disableNode.restore(); - }); - - it('allows an unused node type to be disabled',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - return index.disableNode("5678").then(function(info) { - registry.disableNode.calledOnce.should.be.true(); - registry.disableNode.calledWith("5678").should.be.true(); - info.should.eql(randomNodeInfo); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('prevents disabling a node type that is in use',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.disabledNode("test"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('prevents disabling a node type that is unknown',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.disableNode("doesnotexist"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe('allows modules to be removed from the registry', function() { - var randomNodeInfo = {id:"5678",types:["random"]}; - var randomModuleInfo = { - name:"random", - nodes: [randomNodeInfo] - }; - - before(function() { - sinon.stub(registry,"getNodeInfo").callsFake(function(id) { - if (id == "node-red/foo") { - return {id:"1234",types:["test"]}; - } else if (id == "doesnotexist") { - return null; - } else { - return randomNodeInfo; - } - }); - sinon.stub(registry,"getModuleInfo").callsFake(function(module) { - if (module == "node-red") { - return {nodes:[{name:"foo"}]}; - } else if (module == "doesnotexist") { - return null; - } else { - return randomModuleInfo; - } - }); - sinon.stub(registry,"removeModule").callsFake(function(id) { - return randomModuleInfo; - }); - }); - after(function() { - registry.getNodeInfo.restore(); - registry.getModuleInfo.restore(); - registry.removeModule.restore(); - }); - - it('prevents removing a module that is in use',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.removeModule("node-red"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('prevents removing a module that is unknown',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.removeModule("doesnotexist"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png b/test/unit/@node-red/runtime/lib/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png deleted file mode 100644 index e38f3914600901b736f5fa18786ee11be6d41c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6icy_X9x!n)NrJ90QsB+9+AZi z3~X;em{G3O!W1YdS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|Bb%p- zV~9uR+N-+$hZ97Oeyp#Y$RU!&XX#m>@|$agvYL9Jz$O6}72y^gZVi>ZMeaH^t*Q|! zLhVk<0xdko8zYVo#8M-LBBd8b>1kw|i?FO)bWqsy_rwQg27N4y zeX6pqO$_Cex^^A7_NsS@+=bOASA^wN0&GKN(hlAguRU&q- jTBY?`*L>xN+|S&+7B*edvmQhOgN?z{)z4*}Q$iB}+9!`i diff --git a/test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png b/test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png deleted file mode 100644 index 59a29af14..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png +++ /dev/null @@ -1,3 +0,0 @@ -This file exists just to ensure the 'icons' directory is in the repository. -TODO: a future test needs to ensure the right icon files are loaded - this - directory can be used for that diff --git a/test/unit/@node-red/runtime/lib/plugins_spec.js b/test/unit/@node-red/runtime/lib/plugins_spec.js deleted file mode 100644 index a78de643c..000000000 --- a/test/unit/@node-red/runtime/lib/plugins_spec.js +++ /dev/null @@ -1,13 +0,0 @@ -const should = require("should"); -const sinon = require("sinon"); -const NR_TEST_UTILS = require("nr-test-utils"); - -const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/plugins"); - -describe("runtime/plugins",function() { - - it.skip("delegates all functions to registry module", function() { - // There's no easy way to test this as we can't stub the registry functions - // before the plugin module gets a reference to them - }) -}); diff --git a/test/unit/@node-red/runtime/lib/settings_spec.js b/test/unit/@node-red/runtime/lib/settings_spec.js deleted file mode 100644 index 51c190fea..000000000 --- a/test/unit/@node-red/runtime/lib/settings_spec.js +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/settings"); - - -describe("runtime/settings", function() { - - afterEach(function() { - settings.reset(); - }); - - it('wraps the user settings as read-only properties', function() { - var userSettings = { - a: 123, - b: "test", - c: [1,2,3] - } - settings.init(userSettings); - - settings.available().should.be.false(); - - settings.a.should.equal(123); - settings.b.should.equal("test"); - settings.c.should.be.an.Array(); - settings.c.should.have.lengthOf(3); - - settings.get("a").should.equal(123); - settings.get("b").should.equal("test"); - settings.get("c").should.be.an.Array(); - settings.get("c").should.have.lengthOf(3); - - /*jshint immed: false */ - (function() { - settings.a = 456; - }).should.throw(); - - settings.c.push(5); - settings.c.should.be.an.Array(); - settings.c.should.have.lengthOf(4); - - /*jshint immed: false */ - (function() { - settings.set("a",456); - }).should.throw(); - - /*jshint immed: false */ - (function() { - settings.set("a",456); - }).should.throw(); - - /*jshint immed: false */ - (function() { - settings.get("unknown"); - }).should.throw(); - - /*jshint immed: false */ - (function() { - settings.set("unknown",456); - }).should.throw(); - - }); - - it('loads global settings from storage', function(done) { - var userSettings = { - a: 123, - b: "test", - c: [1,2,3] - } - var savedSettings = null; - var saveCount = 0; - var storage = { - getSettings: function() { - return Promise.resolve({globalA:789}); - }, - saveSettings: function(settings) { - saveCount++; - savedSettings = settings; - return Promise.resolve(); - } - } - settings.init(userSettings); - - settings.available().should.be.false(); - - /*jshint immed: false */ - (function() { - settings.get("unknown"); - }).should.throw(); - settings.load(storage).then(function() { - settings.available().should.be.true(); - settings.get("globalA").should.equal(789); - settings.set("globalA","abc").then(function() { - savedSettings.globalA.should.equal("abc"); - saveCount.should.equal(1); - settings.set("globalA","abc").then(function() { - savedSettings.globalA.should.equal("abc"); - // setting to existing value should not trigger save - saveCount.should.equal(1); - done(); - }); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('removes persistent settings when reset', function() { - var userSettings = { - a: 123, - b: "test", - c: [1,2,3] - } - settings.init(userSettings); - - settings.available().should.be.false(); - - settings.should.have.property("a",123); - settings.should.have.property("b","test"); - settings.c.should.be.an.Array(); - settings.c.should.have.lengthOf(3); - - settings.reset(); - - settings.should.not.have.property("a"); - settings.should.not.have.property("d"); - settings.should.not.have.property("c"); - - }); - - it('registers node settings and exports them', function() { - var userSettings = {}; - settings.init(userSettings); - settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}, injectSize:{value:"100", exportable:true}} ); - settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:false}, mqttSize:{value:"50", exportable:true}} ); - settings.registerNodeSettings("http request", {httpRequest1:{value:"a1", exportable:true}} ); - settings.registerNodeSettings(" http--request<> ", {httpRequest2:{value:"a2", exportable:true}} ); - settings.registerNodeSettings("_http_request_", {httpRequest3:{value:"a3", exportable:true}} ); - settings.registerNodeSettings("mQtT", {mQtTColor:{value:"purple", exportable:true}} ); - settings.registerNodeSettings("abc123", {abc123:{value:"def456", exportable:true}} ); - settings.registerNodeSettings("noValue", {noValueHasValue:{value:"123", exportable:true}, noValueNoValue:{exportable:true}} ); - - var safeSettings = {}; - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "red"); - safeSettings.should.have.property("injectSize", "100"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.have.property("mqttSize", "50"); - safeSettings.should.have.property("httpRequest1", "a1"); - safeSettings.should.have.property("httpRequest2", "a2"); - safeSettings.should.have.property("httpRequest3", "a3"); - safeSettings.should.have.property("mQtTColor", "purple"); - safeSettings.should.have.property("abc123", "def456"); - - safeSettings.should.have.property("noValueHasValue", "123"); - safeSettings.should.not.have.property("noValueNoValue"); - }); - - it('prohibits registering the property whose name do not start with type name', function() { - var userSettings = {}; - settings.init(userSettings); - (function() { - settings.registerNodeSettings("inject", {color:{value:"red", exportable:true}} ); - }).should.throw(); - (function() { - settings.registerNodeSettings("_a_b_1_", {ab1Color:{value:"red", exportable:true}} ); - }).should.throw(); - (function() { - settings.registerNodeSettings("AB2", {AB2Color:{value:"red", exportable:true}} ); - }).should.throw(); - (function() { - settings.registerNodeSettings("abcDef", {abcColor:{value:"red", exportable:true}} ); - }).should.throw(); - var safeSettings = {}; - settings.exportNodeSettings(safeSettings); - safeSettings.should.not.have.property("color"); - safeSettings.should.not.have.property("ab1Color", "blue"); - safeSettings.should.not.have.property("AB2Color"); - safeSettings.should.not.have.property("abcColor"); - }); - - it('overwrites node settings with user settings', function() { - var userSettings = { - injectColor: "green", - mqttColor: "yellow", - abColor: [1,2,3] - } - settings.init(userSettings); - settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); - settings.registerNodeSettings("ab", {abColor:{value:"red", exportable:false}} ); - var safeSettings = {}; - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "green"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.not.have.property("abColor"); - }); - - it('disables/enables node settings', function() { - var userSettings = {}; - settings.init(userSettings); - - var safeSettings = {}; - settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); - settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:true}} ); - settings.registerNodeSettings("http request", {httpRequestColor:{value:"yellow", exportable:true}} ); - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "red"); - safeSettings.should.have.property("mqttColor", "purple"); - safeSettings.should.have.property("httpRequestColor", "yellow"); - - safeSettings = {}; - var types = ["inject", "mqtt"]; - settings.disableNodeSettings(types); - settings.exportNodeSettings(safeSettings); - safeSettings.should.not.have.property("injectColor"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.have.property("httpRequestColor", "yellow"); - - safeSettings = {}; - types = ["inject"]; - settings.enableNodeSettings(types); - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "red"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.have.property("httpRequestColor", "yellow"); - }); - - - it('delete global setting', function() { - // read-only - var localSettings = {a:1}; - // read-write - var globalSettings = {b:2}; - var storage = { - getSettings: function() { - return Promise.resolve(globalSettings); - }, - saveSettings: function() { - return Promise.resolve(); - } - } - settings.init(localSettings); - return settings.load(storage).then(function() { - settings.get('a').should.eql(1); - settings.get('b').should.eql(2); - return settings.delete('b') - }).then(function() { - should.not.exist(settings.get('b')); - }) - }); - - it('refused to delete local setting', function(done) { - // read-only - var localSettings = {a:1}; - // read-write - var globalSettings = {b:2}; - var storage = { - getSettings: function() { - return Promise.resolve(globalSettings); - } - } - settings.init(localSettings); - settings.load(storage).then(function() { - settings.get('a').should.eql(1); - settings.get('b').should.eql(2); - try { - settings.delete('a'); - return done("Did not throw error"); - } catch(err) { - // expected - } - done(); - }).catch(done) - }); - - - it('get user settings', function() { - var userSettings = { - admin: {a:1} - } - var storage = { - getSettings: function() { - return Promise.resolve({a:1,users:userSettings}); - } - } - settings.init(userSettings); - return settings.load(storage).then(function() { - var result = settings.getUserSettings('admin'); - result.should.eql(userSettings.admin); - // Check it has been cloned - result.should.not.equal(userSettings.admin); - }) - }) - it('set user settings', function() { - var userSettings = { - admin: {a:1} - } - var savedSettings; - var storage = { - getSettings: function() { - return Promise.resolve({c:3,users:userSettings}); - }, - saveSettings: function(s) { - savedSettings = s; - return Promise.resolve(); - } - } - settings.init(userSettings); - return settings.load(storage).then(function() { - return settings.setUserSettings('admin',{b:2}) - }).then(function() { - savedSettings.should.have.property("c",3); - savedSettings.should.have.property('users'); - savedSettings.users.should.eql({admin:{b:2}}) - }) - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/index_spec.js b/test/unit/@node-red/runtime/lib/storage/index_spec.js deleted file mode 100644 index dd9fa6e9b..000000000 --- a/test/unit/@node-red/runtime/lib/storage/index_spec.js +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var paff = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var storage = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/index"); - -describe("red/storage/index", function() { - - it('rejects the promise when settings suggest loading a bad module', function(done) { - - var wrongModule = { - settings:{ - storageModule : "thisaintloading" - } - }; - - storage.init(wrongModule).then( function() { - var one = 1; - var zero = 0; - try { - zero.should.equal(one, "The initialization promise should never get resolved"); - } catch(err) { - done(err); - } - }).catch(function(e) { - done(); //successfully rejected promise - }); - }); - - it('non-string storage module', function(done) { - var initSetsMeToTrue = false; - - var moduleWithBooleanSettingInit = { - init : function() { - initSetsMeToTrue = true; - } - }; - - var setsBooleanModule = { - settings: { - storageModule : moduleWithBooleanSettingInit - } - }; - - storage.init(setsBooleanModule); - initSetsMeToTrue.should.be.true(); - done(); - }); - - it('respects storage interface', function(done) { - var calledFlagGetFlows = false; - var calledFlagGetCredentials = false; - var calledFlagGetAllFlows = false; - var calledInit = false; - var calledFlagGetSettings = false; - var calledFlagGetSessions = false; - - var interfaceCheckerModule = { - init : function (settings) { - settings.should.be.an.Object(); - calledInit = true; - }, - getFlows : function() { - calledFlagGetFlows = true; - return Promise.resolve([]); - }, - saveFlows : function (flows) { - flows.should.be.an.Array(); - flows.should.have.lengthOf(0); - return Promise.resolve(""); - }, - getCredentials : function() { - calledFlagGetCredentials = true; - return Promise.resolve({}); - }, - saveCredentials : function(credentials) { - credentials.should.be.true(); - }, - getSettings : function() { - calledFlagGetSettings = true; - }, - saveSettings : function(settings) { - settings.should.be.true(); - }, - getSessions : function() { - calledFlagGetSessions = true; - }, - saveSessions : function(sessions) { - sessions.should.be.true(); - }, - getAllFlows : function() { - calledFlagGetAllFlows = true; - }, - getFlow : function(fn) { - fn.should.equal("name"); - }, - saveFlow : function(fn, data) { - fn.should.equal("name"); - data.should.be.true(); - }, - getLibraryEntry : function(type, path) { - type.should.be.true(); - path.should.equal("name"); - }, - saveLibraryEntry : function(type, path, meta, body) { - type.should.be.true(); - path.should.equal("name"); - meta.should.be.true(); - body.should.be.true(); - } - }; - - var moduleToLoad = { - settings: { - storageModule : interfaceCheckerModule - } - }; - - var promises = []; - storage.init(moduleToLoad); - promises.push(storage.getFlows()); - promises.push(storage.saveFlows({flows:[],credentials:{}})); - storage.getSettings(); - storage.saveSettings(true); - storage.getSessions(); - storage.saveSessions(true); - storage.getAllFlows(); - storage.getFlow("name"); - storage.saveFlow("name", true); - storage.getLibraryEntry(true, "name"); - storage.saveLibraryEntry(true, "name", true, true); - - Promise.all(promises).then(function() { - try { - calledInit.should.be.true(); - calledFlagGetFlows.should.be.true(); - calledFlagGetCredentials.should.be.true(); - calledFlagGetAllFlows.should.be.true(); - done(); - } catch(err) { - done(err); - } - }); - }); - - describe('respects deprecated flow library functions', function() { - - var savePath; - var saveContent; - var saveMeta; - var saveType; - - var interfaceCheckerModule = { - init : function (settings) { - settings.should.be.an.Object(); - }, - getLibraryEntry : function(type, path) { - if (type === "flows") { - if (path === "/" || path === "\\") { - return Promise.resolve(["a",{fn:"test.json"}]); - } else if (path == "/a" || path == "\\a") { - return Promise.resolve([{fn:"test2.json"}]); - } else if (path == paff.join("","a","test2.json")) { - return Promise.resolve("test content"); - } - } - }, - saveLibraryEntry : function(type, path, meta, body) { - saveType = type; - savePath = path; - saveContent = body; - saveMeta = meta; - return Promise.resolve(); - } - }; - - var moduleToLoad = { - settings: { - storageModule : interfaceCheckerModule - } - }; - before(function() { - storage.init(moduleToLoad); - }); - it('getAllFlows',function(done) { - storage.getAllFlows().then(function (res) { - try { - res.should.eql({ d: { a: { f: ['test2'] } }, f: [ 'test' ] }); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('getFlow',function(done) { - storage.getFlow(paff.join("a","test2.json")).then(function(res) { - try { - res.should.eql("test content"); - done(); - } catch(err) { - done(err); - } - }); - }); - - it ('saveFlow', function (done) { - storage.saveFlow(paff.join("a","test2.json"),"new content").then(function(res) { - try { - savePath.should.eql(paff.join("a","test2.json")); - saveContent.should.eql("new content"); - saveMeta.should.eql({}); - saveType.should.eql("flows"); - done(); - } catch(err) { - done(err); - } - }); - - }); - }); - - describe('handles missing settings/sessions interface', function() { - before(function() { - var interfaceCheckerModule = { - init : function () {} - }; - storage.init({settings:{storageModule: interfaceCheckerModule}}); - }); - - it('defaults missing getSettings',function(done) { - storage.getSettings().then(function(settings) { - should.not.exist(settings); - done(); - }); - }); - it('defaults missing saveSettings',function(done) { - storage.saveSettings({}).then(function() { - done(); - }); - }); - it('defaults missing getSessions',function(done) { - storage.getSessions().then(function(settings) { - should.not.exist(settings); - done(); - }); - }); - it('defaults missing saveSessions',function(done) { - storage.saveSessions({}).then(function() { - done(); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js deleted file mode 100644 index 65826c9f4..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js +++ /dev/null @@ -1,518 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); -var sinon = require('sinon'); -var NR_TEST_UTILS = require("nr-test-utils"); -var process = require("process"); - -var localfilesystem = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem"); -var log = NR_TEST_UTILS.require("@node-red/util").log; - -describe('storage/localfilesystem', function() { - var mockRuntime = { - log:{ - _:function() { return "placeholder message"}, - info: function() { }, - warn: function() { }, - trace: function() {} - } - }; - var userDir = path.join(__dirname,".testUserHome"); - var testFlow = [{"type":"tab","id":"d8be2a6d.2741d8","label":"Sheet 1"}]; - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - - it('should initialise the user directory',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(path.join(userDir,"lib")).should.be.true(); - fs.existsSync(path.join(userDir,"lib",'flows')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - - it('should set userDir to NRH if .config.json presents',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - fs.mkdirSync(process.env.NODE_RED_HOME); - fs.writeFileSync(path.join(process.env.NODE_RED_HOME,".config.json"),"{}","utf8"); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib")).should.be.true(); - fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib",'flows')).should.be.true(); - settings.userDir.should.equal(process.env.NODE_RED_HOME); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should set userDir to HOMEPATH/.node-red if .config.json presents',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - var oldHOMEPATH = process.env.HOMEPATH; - process.env.HOMEPATH = path.join(userDir,"HOMEPATH"); - fs.mkdirSync(process.env.HOMEPATH); - fs.mkdirSync(path.join(process.env.HOMEPATH,".node-red")); - fs.writeFileSync(path.join(process.env.HOMEPATH,".node-red",".config.json"),"{}","utf8"); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib")).should.be.true(); - fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib",'flows')).should.be.true(); - settings.userDir.should.equal(path.join(process.env.HOMEPATH,".node-red")); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.NODE_HOMEPATH = oldHOMEPATH; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should set userDir to HOME/.node-red',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - var oldHOME = process.env.HOME; - process.env.HOME = path.join(userDir,"HOME"); - var oldHOMEPATH = process.env.HOMEPATH; - process.env.HOMEPATH = path.join(userDir,"HOMEPATH"); - - fs.mkdirSync(process.env.HOME); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.HOME,".node-red","lib")).should.be.true(); - fs.existsSync(path.join(process.env.HOME,".node-red","lib",'flows')).should.be.true(); - settings.userDir.should.equal(path.join(process.env.HOME,".node-red")); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOME = oldHOME; - process.env.HOMEPATH = oldHOMEPATH; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should set userDir to USERPROFILE/.node-red',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - var oldHOME = process.env.HOME; - process.env.HOME = ""; - var oldHOMEPATH = process.env.HOMEPATH; - process.env.HOMEPATH = path.join(userDir,"HOMEPATH"); - var oldUSERPROFILE = process.env.USERPROFILE; - process.env.USERPROFILE = path.join(userDir,"USERPROFILE"); - - fs.mkdirSync(process.env.USERPROFILE); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib")).should.be.true(); - fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib",'flows')).should.be.true(); - settings.userDir.should.equal(path.join(process.env.USERPROFILE,".node-red")); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOME = oldHOME; - process.env.HOMEPATH = oldHOMEPATH; - process.env.USERPROFILE = oldUSERPROFILE; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should handle missing flow file',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - fs.existsSync(flowFilePath).should.be.false(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle empty flow file, no backup',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - fs.closeSync(fs.openSync(flowFilePath, 'w')); - fs.existsSync(flowFilePath).should.be.true(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle empty flow file, restores backup',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - fs.closeSync(fs.openSync(flowFilePath, 'w')); - fs.existsSync(flowFilePath).should.be.true(); - fs.existsSync(flowFileBackupPath).should.be.false(); - fs.writeFileSync(flowFileBackupPath,JSON.stringify(testFlow)); - fs.existsSync(flowFileBackupPath).should.be.true(); - setTimeout(function() { - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - },50); - }).catch(function(err) { - done(err); - }); - }); - - it('should save flows to the default file',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - fs.existsSync(flowFilePath).should.be.false(); - fs.existsSync(flowFileBackupPath).should.be.false(); - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(flowFilePath).should.be.true(); - fs.existsSync(flowFileBackupPath).should.be.false(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should save flows to the specified file',function(done) { - var defaultFlowFile = 'flows_'+require('os').hostname()+'.json'; - var defaultFlowFilePath = path.join(userDir,defaultFlowFile); - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.false(); - - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.true(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should format the flows file when flowFilePretty specified',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,flowFilePretty:true,getUserSettings: () => {{}}}, mockRuntime).then(function() { - localfilesystem.saveFlows(testFlow).then(function() { - var content = fs.readFileSync(flowFilePath,"utf8"); - content.split("\n").length.should.be.above(1); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should fsync the flows file',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - localfilesystem.init({editorTheme:{projects:{enabled:false}},userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - sinon.spy(fs,"fsync"); - localfilesystem.saveFlows(testFlow).then(function() { - fs.fsync.callCount.should.be.greaterThan(0); - fs.fsync.restore(); - done(); - }).catch(function(err) { - fs.fsync.restore(); - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should log fsync errors and continue',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - sinon.stub(fs,"fsync").callsFake(function(fd, cb) { - cb(new Error()); - }); - sinon.spy(log,"warn"); - localfilesystem.saveFlows(testFlow).then(function() { - fs.fsync.callCount.should.be.greaterThan(0); - log.warn.restore(); - fs.fsync.callCount.should.be.greaterThan(0); - fs.fsync.restore(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should backup the flows file', function(done) { - var defaultFlowFile = 'flows_'+require('os').hostname()+'.json'; - var defaultFlowFilePath = path.join(userDir,defaultFlowFile); - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.false(); - fs.existsSync(flowFileBackupPath).should.be.false(); - - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(flowFileBackupPath).should.be.false(); - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.true(); - var content = fs.readFileSync(flowFilePath,'utf8'); - var testFlow2 = [{"type":"tab","id":"bc5672ad.2741d8","label":"Sheet 2"}]; - - localfilesystem.saveFlows(testFlow2).then(function() { - fs.existsSync(flowFileBackupPath).should.be.true(); - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.true(); - var backupContent = fs.readFileSync(flowFileBackupPath,'utf8'); - content.should.equal(backupContent); - var content2 = fs.readFileSync(flowFilePath,'utf8'); - content2.should.not.equal(backupContent); - done(); - - }).catch(function(err) { - done(err); - }); - - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - - - }); - - it('should handle missing credentials', function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(credFile).should.be.false(); - - localfilesystem.getCredentials().then(function(creds) { - creds.should.eql({}); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle credentials', function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - - fs.existsSync(credFile).should.be.false(); - - var credentials = {"abc":{"type":"creds"}}; - - localfilesystem.saveCredentials(credentials).then(function() { - fs.existsSync(credFile).should.be.true(); - localfilesystem.getCredentials().then(function(creds) { - creds.should.eql(credentials); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - - it('should backup existing credentials', function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - var credFileBackup = path.join(userDir,".test_cred.json.backup"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - - fs.writeFileSync(credFile,"{}","utf8"); - - fs.existsSync(credFile).should.be.true(); - fs.existsSync(credFileBackup).should.be.false(); - - var credentials = {"abc":{"type":"creds"}}; - - localfilesystem.saveCredentials(credentials).then(function() { - fs.existsSync(credFile).should.be.true(); - fs.existsSync(credFileBackup).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should format the creds file when flowFilePretty specified',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath, flowFilePretty:true,getUserSettings: () => {{}}}, mockRuntime).then(function() { - - fs.existsSync(credFile).should.be.false(); - - var credentials = {"abc":{"type":"creds"}}; - - localfilesystem.saveCredentials(credentials).then(function() { - fs.existsSync(credFile).should.be.true(); - var content = fs.readFileSync(credFile,"utf8"); - content.split("\n").length.should.be.above(1); - localfilesystem.getCredentials().then(function(creds) { - creds.should.eql(credentials); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle flow file in random unc path and non-existent subfolder',function(done) { - // only test on win32 - if (process.platform !== 'win32') { - console.log('skipped test as not win32'); - done(); - return; - } - - // get a real windows path - var flowFile = path.win32.resolve(userDir+'/some/random/path'); - var rootdir = path.win32.resolve(userDir+'/some'); - // make it into a local UNC path - flowFile = flowFile.replace('C:\\', '\\\\localhost\\c$\\'); - localfilesystem.init({userDir:userDir, flowFile:flowFile}, mockRuntime).then(function() { - fs.existsSync(flowFile).should.be.false(); - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(flowFile).should.be.true(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - // cleanup - fs.removeSync(rootdir); - done(); - }).catch(function(err) { - // cleanup - fs.removeSync(rootdir); - done(err); - }); - }).catch(function(err) { - // cleanup - fs.removeSync(rootdir); - done(err); - }); - }).catch(function(err) { - // cleanup - fs.removeSync(rootdir); - done(err); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js deleted file mode 100644 index 69b6e3da6..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); -var NR_TEST_UTILS = require("nr-test-utils"); - -var localfilesystemLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/library"); - -describe('storage/localfilesystem/library', function() { - var userDir = path.join(__dirname,".testUserHome"); - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - - it('should return an empty list of library objects',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - localfilesystemLibrary.getLibraryEntry('object','').then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return an empty list of library objects (path=/)',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - localfilesystemLibrary.getLibraryEntry('object','/').then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return an error for a non-existent library object',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - localfilesystemLibrary.getLibraryEntry('object','A/B').then(function(flows) { - should.fail(null,null,"non-existent flow"); - }).catch(function(err) { - should.exist(err); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - - function createObjectLibrary(type) { - type = type || "object"; - var objLib = path.join(userDir, "lib", type); - try { - fs.mkdirSync(objLib); - } catch (err) { - } - fs.mkdirSync(path.join(objLib, "A")); - fs.mkdirSync(path.join(objLib, "B")); - fs.mkdirSync(path.join(objLib, "B", "C")); - fs.mkdirSync(path.join(objLib, "D")); - if (type === "functions" || type === "object") { - fs.writeFileSync(path.join(objLib, "file1.js"), "// abc: def\n// not a metaline \n\n Hi", 'utf8'); - fs.writeFileSync(path.join(objLib, "B", "file2.js"), "// ghi: jkl\n// not a metaline \n\n Hi", 'utf8'); - fs.writeFileSync(path.join(objLib, "D", "file3.js"), "// mno: 日本語テスト\n\nこんにちわ", 'utf8'); - } - if (type === "flows" || type === "object") { - fs.writeFileSync(path.join(objLib, "B", "flow.json"), "Hi", 'utf8'); - } - } - - it('should return a directory listing of library objects', function (done) { - localfilesystemLibrary.init({userDir: userDir}).then(function () { - createObjectLibrary(); - - localfilesystemLibrary.getLibraryEntry('object', '').then(function (flows) { - flows.should.eql([ 'A', 'B', 'D', { abc: 'def', fn: 'file1.js' }]); - localfilesystemLibrary.getLibraryEntry('object', 'B').then(function (flows) { - flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' }, { fn: 'flow.json' }]); - localfilesystemLibrary.getLibraryEntry('object', 'B/C').then(function (flows) { - flows.should.eql([]); - localfilesystemLibrary.getLibraryEntry('object', 'D').then(function (flows) { - flows.should.eql([{ mno: '日本語テスト', fn: 'file3.js' }]); - done(); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }); - - it('should load a flow library object with .json unspecified', function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("flows"); - localfilesystemLibrary.getLibraryEntry('flows','B/flow').then(function(flows) { - flows.should.eql("Hi"); - done(); - }).catch(function(err) { - done(err); - }); - }); - - }); - - it('should return a library object',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary(); - localfilesystemLibrary.getLibraryEntry('object','B/file2.js').then(function(body) { - body.should.eql("// not a metaline \n\n Hi"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return a newly saved library function',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("functions"); - localfilesystemLibrary.getLibraryEntry('functions','B').then(function(flows) { - flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' } ]); - var ft = path.join("B","D","file3.js"); - localfilesystemLibrary.saveLibraryEntry('functions',ft,{mno:'pqr'},"// another non meta line\n\n Hi There").then(function() { - setTimeout(function() { - localfilesystemLibrary.getLibraryEntry('functions',path.join("B","D")).then(function(flows) { - flows.should.eql([ { mno: 'pqr', fn: 'file3.js' } ]); - localfilesystemLibrary.getLibraryEntry('functions',ft).then(function(body) { - body.should.eql("// another non meta line\n\n Hi There"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }) - }, 50); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return a newly saved library flow',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("flows"); - localfilesystemLibrary.getLibraryEntry('flows','B').then(function(flows) { - flows.should.eql([ 'C', {fn:'flow.json'} ]); - var ft = path.join("B","D","file3"); - localfilesystemLibrary.saveLibraryEntry('flows',ft,{mno:'pqr'},"Hi").then(function() { - setTimeout(function() { - localfilesystemLibrary.getLibraryEntry('flows',path.join("B","D")).then(function(flows) { - flows.should.eql([ { mno: 'pqr', fn: 'file3.json' } ]); - localfilesystemLibrary.getLibraryEntry('flows',ft+".json").then(function(body) { - body.should.eql("Hi"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }) - }, 50); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return a newly saved library flow (multi-byte character)',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("flows"); - localfilesystemLibrary.getLibraryEntry('flows','B').then(function(flows) { - flows.should.eql([ 'C', {fn:'flow.json'} ]); - var ft = path.join("B","D","file4"); - localfilesystemLibrary.saveLibraryEntry('flows',ft,{mno:'pqr'},"こんにちわこんにちわこんにちわ").then(function() { - setTimeout(function() { - localfilesystemLibrary.getLibraryEntry('flows',path.join("B","D")).then(function(flows) { - flows.should.eql([ { mno: 'pqr', fn: 'file4.json' } ]); - localfilesystemLibrary.getLibraryEntry('flows',ft+".json").then(function(body) { - body.should.eql("こんにちわこんにちわこんにちわ"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }) - }, 50); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js deleted file mode 100644 index ecf24cf40..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("storage/localfilesystem/projects/Project", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}) diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js deleted file mode 100644 index 3fab5a45c..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var NR_TEST_UTILS = require("nr-test-utils"); -var defaultFileSet = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet"); - -describe('storage/localfilesystem/projects/defaultFileSet', function() { - var runtime = { - i18n: { - "_": function(name) { - return name; - } - } - }; - it('generates package.json for a project', function() { - var generated = defaultFileSet["package.json"]({ - name: "A TEST NAME", - summary: "A TEST SUMMARY", - files: { - flow: "MY FLOW FILE", - credentials: "MY CREDENTIALS FILE" - } - }, runtime); - - var parsed = JSON.parse(generated); - parsed.should.have.property('name',"A TEST NAME"); - parsed.should.have.property('description',"A TEST SUMMARY"); - parsed.should.have.property('node-red'); - parsed['node-red'].should.have.property('settings'); - parsed['node-red'].settings.should.have.property('flowFile',"MY FLOW FILE"); - parsed['node-red'].settings.should.have.property('credentialsFile',"MY CREDENTIALS FILE"); - }); - - it('generates README.md for a project', function() { - var generated = defaultFileSet["README.md"]({ - name: "A TEST NAME", - summary: "A TEST SUMMARY" - }, runtime); - generated.should.match(/A TEST NAME/); - generated.should.match(/A TEST SUMMARY/); - }); - it('generates .gitignore for a project', function() { - var generated = defaultFileSet[".gitignore"]({ - name: "A TEST NAME", - summary: "A TEST SUMMARY" - }, runtime); - generated.length.should.be.greaterThan(0); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js deleted file mode 100644 index c1617bf90..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var authCache = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache") - -describe("localfilesystem/projects/git/authCache", function() { - - beforeEach(function() { - authCache.init(); - }); - afterEach(function() { - authCache.init(); - }); - - it('sets/clears auth details for a given project/remote/user', function() { - should.not.exist(authCache.get("project","remote1","user1")); - should.not.exist(authCache.get("project","remote1","user2")); - - authCache.set("project","remote1","user1",{foo1:"bar1"}); - authCache.set("project","remote1","user2",{foo2:"bar2"}); - - var result = authCache.get("project","remote1","user1"); - result.should.have.property("foo1","bar1"); - - result = authCache.get("project","remote1","user2"); - result.should.have.property("foo2","bar2"); - - authCache.clear("project","remote1","user1"); - should.not.exist(authCache.get("project","remote1","user1")); - should.exist(authCache.get("project","remote1","user2")); - - }); - - - it('clears auth details for all users on a given project/remote', function() { - - authCache.set("project","remote1","user1",{foo1:"bar1"}); - authCache.set("project","remote1","user2",{foo2:"bar2"}); - authCache.set("project","remote2","user1",{foo3:"bar3"}); - - should.exist(authCache.get("project","remote1","user1")); - should.exist(authCache.get("project","remote1","user2")); - should.exist(authCache.get("project","remote2","user1")); - - authCache.clear("project","remote1"); - should.not.exist(authCache.get("project","remote1","user1")); - should.not.exist(authCache.get("project","remote1","user2")); - should.exist(authCache.get("project","remote2","user1")); - }); - - it('clears auth details for all remotes/users on a given project', function() { - - authCache.set("project1","remote1","user1",{foo1:"bar1"}); - authCache.set("project1","remote1","user2",{foo2:"bar2"}); - authCache.set("project2","remote2","user1",{foo3:"bar3"}); - - should.exist(authCache.get("project1","remote1","user1")); - should.exist(authCache.get("project1","remote1","user2")); - should.exist(authCache.get("project2","remote2","user1")); - - authCache.clear("project2"); - should.exist(authCache.get("project1","remote1","user1")); - should.exist(authCache.get("project1","remote1","user2")); - should.not.exist(authCache.get("project2","remote2","user1")); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js deleted file mode 100644 index 9b789a66b..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var path = require("path"); -var os = require("os"); -var should = require("should"); -var sinon = require("sinon"); -var child_process = require("child_process"); -var fs = require("fs-extra"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var authServer = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer"); - - -var sendPrompt = function(localPath, prompt) { - return new Promise(function(resolve,reject) { - var response; - var socket = net.connect(localPath, function() { - socket.on('data', function(data) { response = data; socket.end() }); - socket.on('end', function() { - resolve(response); - }); - socket.on('error',reject); - socket.write(prompt+"\n", 'utf8'); - }); - socket.setEncoding('utf8'); - }); -} - - -describe("localfilesystem/projects/git/authServer", function() { - it("listens for user/pass prompts and returns provided auth", function(done) { - authServer.ResponseServer({username: "TEST_USER", password: "TEST_PASS"}).then(function(rs) { - sendPrompt(rs.path,"Username").then(function(response) { - response.should.eql("TEST_USER"); - return sendPrompt(rs.path,"Password"); - }).then(function(response) { - response.should.eql("TEST_PASS"); - }).then(() => { - rs.close(); - done(); - }).catch(function(err) { - rs.close(); - done(err); - }) - - }) - }); - - it("listens for ssh prompts and returns provided auth", function(done) { - authServer.ResponseSSHServer({passphrase: "TEST_PASSPHRASE"}).then(function(rs) { - sendPrompt(rs.path,"The").then(function(response) { - // TODO: - response.should.eql("yes"); - return sendPrompt(rs.path,"Enter"); - }).then(function(response) { - response.should.eql("TEST_PASSPHRASE"); - }).then(() => { - rs.close(); - done(); - }).catch(function(err) { - rs.close(); - done(err); - }) - - }) - }) -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js deleted file mode 100644 index c80cb8ad1..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var path = require("path"); -var os = require("os"); -var should = require("should"); -var sinon = require("sinon"); -var child_process = require("child_process"); -var fs = require("fs-extra"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var authWriter = NR_TEST_UTILS.resolve("@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter"); - -function getListenPath() { - var seed = (0x100000+Math.random()*0x999999).toString(16); - var fn = 'node-red-git-askpass-'+seed+'-sock'; - var listenPath; - if (process.platform === 'win32') { - listenPath = '\\\\.\\pipe\\'+fn; - } else { - listenPath = path.join(process.env['XDG_RUNTIME_DIR'] || os.tmpdir(), fn); - } - // console.log(listenPath); - return listenPath; -} - - -describe("localfilesystem/projects/git/authWriter", function() { - it("connects to port and sends passphrase", function(done) { - var receivedData = ""; - var server = net.createServer(function(connection) { - connection.setEncoding('utf8'); - connection.on('data', function(data) { - receivedData += data; - var m = data.indexOf("\n"); - if (m !== -1) { - connection.end(); - } - }); - }); - - var listenPath = getListenPath(); - - server.listen(listenPath, function(ready) { - child_process.exec('"'+process.execPath+'" "'+authWriter+'" "'+listenPath+'" TEST_PHRASE_FOO',{cwd:__dirname}, (error,stdout,stderr) => { - server.close(); - try { - should.not.exist(error); - receivedData.should.eql("TEST_PHRASE_FOO\n"); - done(); - } catch(err) { - done(err); - } - }); - }); - server.on('close', function() { - // console.log("Closing response server"); - fs.removeSync(listenPath); - }); - server.on('error',function(err) { - console.log("ResponseServer unexpectedError:",err.toString()); - server.close(); - done(err); - }); - - - }) -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js deleted file mode 100644 index 773342fa6..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("storage/localfilesystem/projects/git/index", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}) diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js deleted file mode 100644 index f63a20522..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("storage/localfilesystem/projects/index", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}) diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js deleted file mode 100644 index c6d0c533e..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js +++ /dev/null @@ -1,433 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); -var sshkeys = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/ssh"); - -describe("storage/localfilesystem/projects/ssh", function() { - var userDir = path.join(__dirname,".testSSHKeyUserHome"); - var mockSettings = { - userDir: userDir - }; - var mockRuntime = { - log:{ - _:function() { return "placeholder message"}, - info: function() { }, - log: function() { }, - trace: function() { } - } - }; - var oldHOME; - - beforeEach(function(done) { - oldHOME = process.env.HOME; - process.env.HOME = "/tmp/doesnt/exist"; - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - process.env.HOME = oldHOME; - fs.remove(userDir,done); - }); - - it('should create sshkey directory when sshkey initializes', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - sshkeys.init(mockSettings, mockRuntime).then(function() { - var ret = fs.existsSync(sshkeyDirPath); - ret.should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey empty list if there is no sshkey file', function(done) { - var username = 'test'; - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(0); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey list', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filenameList = ['test-key01', 'test-key02']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not get sshkey file if there is only private key', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filenameList = ['test-key01', 'test-key02']; - var onlyPrivateKeyFilenameList = ['test-key03', 'test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of onlyPrivateKeyFilenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var filename of onlyPrivateKeyFilenameList) { - retObj.should.not.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not get sshkey file if there is only public key', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filenameList = ['test-key01', 'test-key02']; - var directoryList = ['test-key03', '.test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of directoryList) { - fs.ensureDirSync(path.join(sshkeyDirPath,filename)); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var directoryname of directoryList) { - retObj.should.not.containEql({ name: directoryname }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey list that does not have directory', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var otherUsername = 'other'; - var filenameList = ['test-key01', 'test-key02']; - var otherUserFilenameList = ['test-key03', 'test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of otherUserFilenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename+".pub"),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var filename of otherUserFilenameList) { - retObj.should.not.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey list that have keys of specified user', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var otherUsername = 'other'; - var filenameList = ['test-key01', 'test-key02']; - var otherUserFilenameList = ['test-key03', 'test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of otherUserFilenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename+".pub"),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var filename of otherUserFilenameList) { - retObj.should.not.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with empty data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - name: 'test-key01' - }; - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with only comment data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01' - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with password data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - password: 'testtest' - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with size data', function(done) { - this.timeout(20000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - size: 4096 - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with password & size data', function(done) { - this.timeout(20000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - password: 'testtest', - size: 4096 - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not generate sshkey file with illegal size data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - size: 1023 - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - done(new Error('Does NOT throw error!')); - }).catch(function(err) { - try { - err.should.have.property('code', 'key_length_too_short'); - done(); - } - catch (error) { - done(error); - } - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not generate sshkey file with illegal password', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - password: 'aa' - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - done(new Error('Does NOT throw error!')); - }).catch(function(err) { - try { - err.should.have.property('code', 'key_passphrase_too_short'); - done(); - } - catch (error) { - done(error); - } - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey file content', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filename = 'test-key01'; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),fileContent,"utf8"); - sshkeys.getSSHKey(username, filename).then(function(retObj) { - retObj.should.be.equal(fileContent); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should delete sshkey files', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filename = 'test-key01'; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),fileContent,"utf8"); - sshkeys.deleteSSHKey(username, filename).then(function() { - fs.existsSync(path.join(sshkeyDirPath,username+'_'+filename)).should.be.false(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+filename+'.pub')).should.be.false(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js deleted file mode 100644 index f3487277b..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var child_process = require('child_process'); -var EventEmitter = require("events"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var keygen = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen") - -describe("localfilesystem/projects/ssh/keygen", function() { - - afterEach(function() { - if (child_process.spawn.restore) { - child_process.spawn.restore(); - } - }) - - it("invokes sshkeygen", function(done) { - var command; - var args; - var opts; - sinon.stub(child_process,"spawn").callsFake(function(_command,_args,_opts) { - _command = command; - _args = args; - _opts = opts; - - var e = new EventEmitter(); - e.stdout = new EventEmitter(); - e.stderr = new EventEmitter(); - setTimeout(function() { - e.stdout.emit("data","result"); - e.emit("close",0); - },50) - return e; - }); - - keygen.generateKey({ - size: 1024, - location: 'location', - comment: 'comment', - password: 'password' - }).then(function(output) { - output.should.equal("result"); - done(); - }).catch(function(err) { - done(err); - }) - }) - - it("reports passphrase too short", function(done) { - var command; - var args; - var opts; - - try { - keygen.generateKey({ - size: 1024, - location: 'location', - comment: 'comment', - password: '123' - }).then(function(output) { - done(new Error("Error not thrown")); - }).catch(function(err) { - done(new Error("Error not thrown")); - }) - } catch(err) { - err.should.have.property("code","key_passphrase_too_short"); - done(); - } - }); - - it("reports key length too short", function(done) { - var command; - var args; - var opts; - try { - keygen.generateKey({ - size: 123, - location: 'location', - comment: 'comment', - password: 'password' - }).then(function(output) { - done(new Error("Error not thrown")); - }).catch(function(err) { - done(new Error("Error not thrown")); - }) - } catch(err) { - err.should.have.property("code","key_length_too_short"); - done(); - - } - }); - - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js deleted file mode 100644 index 685886d67..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); -var localfilesystemSessions = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/sessions"); - -describe('storage/localfilesystem/sessions', function() { - var userDir = path.join(__dirname,".testUserHome"); - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - it('should handle non-existent sessions', function(done) { - var sessionsFile = path.join(userDir,".sessions.json"); - - localfilesystemSessions.init({userDir:userDir}); - fs.existsSync(sessionsFile).should.be.false(); - localfilesystemSessions.getSessions().then(function(sessions) { - sessions.should.eql({}); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle corrupt sessions', function(done) { - var sessionsFile = path.join(userDir,".sessions.json"); - fs.writeFileSync(sessionsFile,"[This is not json","utf8"); - localfilesystemSessions.init({userDir:userDir}); - fs.existsSync(sessionsFile).should.be.true(); - localfilesystemSessions.getSessions().then(function(sessions) { - sessions.should.eql({}); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle sessions', function(done) { - var sessionsFile = path.join(userDir,".sessions.json"); - - localfilesystemSessions.init({userDir:userDir}); - fs.existsSync(sessionsFile).should.be.false(); - - var sessions = {"abc":{"type":"creds"}}; - - localfilesystemSessions.saveSessions(sessions).then(function() { - fs.existsSync(sessionsFile).should.be.true(); - localfilesystemSessions.getSessions().then(function(_sessions) { - _sessions.should.eql(sessions); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js deleted file mode 100644 index bd07ca27f..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -const should = require("should"); -const fs = require('fs-extra'); -const path = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var localfilesystemSettings = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/settings"); - -describe('storage/localfilesystem/settings', function() { - var userDir = path.join(__dirname,".testUserHome"); - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - - it('should handle non-existent settings', function(done) { - var settingsFile = path.join(userDir,".config.json"); - localfilesystemSettings.init({userDir:userDir}).then(function() { - fs.existsSync(settingsFile).should.be.false(); - return localfilesystemSettings.getSettings(); - }).then(function(settings) { - settings.should.eql({}); - done(); - }).catch(err => { done(err)}); - }); - - it('should migrate single config.json to multiple files', function(done) { - var settingsFile = path.join(userDir,".config.json"); - fs.writeFileSync(settingsFile,JSON.stringify({ - nodes:{a:1}, - _credentialSecret: "foo", - users:{b:2}, - projects: {c:3} - }),"utf8"); - - async function checkFile(sectionName, expectedContents) { - const file = path.join(userDir,".config."+sectionName+".json"); - fs.existsSync(file).should.be.true(); - var contents = await fs.readFile(file,'utf8'); - var data = JSON.parse(contents); - data.should.eql(expectedContents) - } - - localfilesystemSettings.init({userDir:userDir}).then(async function() { - // (For now) leave the old settings file in place - fs.existsSync(settingsFile).should.be.true(); - await checkFile("nodes",{a:1}) - await checkFile("users",{b:2}) - await checkFile("projects",{c:3}) - await checkFile("runtime",{_credentialSecret:"foo"}) - done(); - }).catch(err => { done(err)}); - }); - - it('should load separate settings file', async function() { - await fs.writeFile( path.join(userDir,".config.nodes.json"),JSON.stringify({a:1}),"utf8"); - await fs.writeFile( path.join(userDir,".config.users.json"),JSON.stringify({b:2}),"utf8"); - await fs.writeFile( path.join(userDir,".config.projects.json"),JSON.stringify({c:3}),"utf8"); - await fs.writeFile( path.join(userDir,".config.runtime.json"),JSON.stringify({_credentialSecret:"foo"}),"utf8"); - - return localfilesystemSettings.init({userDir:userDir}) - .then(localfilesystemSettings.getSettings) - .then(settings => { - settings.should.eql({ - nodes:{a:1}, - _credentialSecret: "foo", - users:{b:2}, - projects: {c:3} - }) - }) - }); - - it('should write only the files that need writing', async function() { - await fs.writeFile( path.join(userDir,".config.nodes.json"),JSON.stringify({a:1}),"utf8"); - await fs.writeFile( path.join(userDir,".config.users.json"),JSON.stringify({b:2}),"utf8"); - await fs.writeFile( path.join(userDir,".config.projects.json"),JSON.stringify({c:3}),"utf8"); - await fs.writeFile( path.join(userDir,".config.runtime.json"),JSON.stringify({_credentialSecret:"foo"}),"utf8"); - - const fsStatNodes = await fs.stat(path.join(userDir,".config.nodes.json")) - const fsStatUsers = await fs.stat(path.join(userDir,".config.users.json")) - const fsStatProjects = await fs.stat(path.join(userDir,".config.projects.json")) - const fsStatRuntime = await fs.stat(path.join(userDir,".config.runtime.json")) - - return localfilesystemSettings.init({userDir:userDir}).then(function() { - return new Promise(res => { - setTimeout(function() { - res(); - },10) - }); - }).then(() => { - return localfilesystemSettings.saveSettings({ - nodes:{d:4}, - _credentialSecret: "bar", - users:{b:2}, - projects: {c:3} - }) - }).then(async function() { - - const newFsStatNodes = await fs.stat(path.join(userDir,".config.nodes.json")) - const newFsStatUsers = await fs.stat(path.join(userDir,".config.users.json")) - const newFsStatProjects = await fs.stat(path.join(userDir,".config.projects.json")) - const newFsStatRuntime = await fs.stat(path.join(userDir,".config.runtime.json")) - - // Not changed - newFsStatUsers.mtimeMs.should.eql(fsStatUsers.mtimeMs); - newFsStatProjects.mtimeMs.should.eql(fsStatProjects.mtimeMs); - - // Changed - newFsStatNodes.mtimeMs.should.not.eql(fsStatNodes.mtimeMs); - newFsStatRuntime.mtimeMs.should.not.eql(fsStatRuntime.mtimeMs); - - }) - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js deleted file mode 100644 index fa1e4bdb7..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var NR_TEST_UTILS = require("nr-test-utils"); -var util = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/util"); - -describe('storage/localfilesystem/util', function() { - describe('parseJSON', function() { - it('returns parsed JSON', function() { - var result = util.parseJSON('{"a":123}'); - result.should.eql({a:123}); - }) - it('ignores BOM character', function() { - var result = util.parseJSON('\uFEFF{"a":123}'); - result.should.eql({a:123}); - }) - }) -}); diff --git a/test/unit/@node-red/util/index_spec.js b/test/unit/@node-red/util/index_spec.js deleted file mode 100644 index ee46fc504..000000000 --- a/test/unit/@node-red/util/index_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("node-red/red", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}); diff --git a/test/unit/@node-red/util/lib/events_spec.js b/test/unit/@node-red/util/lib/events_spec.js deleted file mode 100644 index 09f5d2ae0..000000000 --- a/test/unit/@node-red/util/lib/events_spec.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("@node-red/util/events", function() { - it('can be required without errors', function() { - NR_TEST_UTILS.require("@node-red/util/lib/events"); - }); - it.skip('more tests needed', function(){}) -}); diff --git a/test/unit/@node-red/util/lib/exec_spec.js b/test/unit/@node-red/util/lib/exec_spec.js deleted file mode 100644 index 9b02fb3f1..000000000 --- a/test/unit/@node-red/util/lib/exec_spec.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var EventEmitter = require("events").EventEmitter; - - -var child_process = require('child_process'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); -var exec = NR_TEST_UTILS.require("@node-red/util/lib/exec"); - -describe("runtime/exec", function() { - var logEvents; - var mockProcess; - const eventLogHandler = function(ev) { - logEvents.push(ev); - } - - beforeEach(function() { - - logEvents = []; - events.on("event-log", eventLogHandler); - - mockProcess = new EventEmitter(); - mockProcess.stdout = new EventEmitter(); - mockProcess.stderr = new EventEmitter(); - sinon.stub(child_process,'spawn').callsFake(function(command,args,options) { - mockProcess._args = {command,args,options}; - return mockProcess; - }); - }); - - afterEach(function() { - events.removeListener("event-log", eventLogHandler); - if (child_process.spawn.restore) { - child_process.spawn.restore(); - } - }); - - it("runs command and resolves on success - no emit", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts).then(function(result) { - command.should.eql(mockProcess._args.command); - args.should.eql(mockProcess._args.args); - opts.should.eql(mockProcess._args.options); - logEvents.length.should.eql(0); - result.code.should.eql(0); - result.stdout.should.eql("123"); - result.stderr.should.eql("abc"); - done(); - }).catch(done); - - mockProcess.stdout.emit('data',"1"); - mockProcess.stderr.emit('data',"a"); - mockProcess.stderr.emit('data',"b"); - mockProcess.stdout.emit('data',"2"); - mockProcess.stdout.emit('data',"3"); - mockProcess.stderr.emit('data',"c"); - mockProcess.emit('close',0); - }); - - it("runs command and resolves on success - emit", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts,true).then(function(result) { - logEvents.length.should.eql(8); - done(); - }).catch(done); - - mockProcess.stdout.emit('data',"1"); - mockProcess.stderr.emit('data',"a"); - mockProcess.stderr.emit('data',"b"); - mockProcess.stdout.emit('data',"2"); - mockProcess.stdout.emit('data',"3"); - mockProcess.stderr.emit('data',"c"); - mockProcess.emit('close',0); - }) - - it("runs command and rejects on error - close", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts).then(function() { - done("Command should have rejected"); - }).catch(function(result) { - result - result.code.should.eql(123); - result.stdout.should.eql("123"); - result.stderr.should.eql("abc"); - done(); - }).catch(done); - - mockProcess.stdout.emit('data',"1"); - mockProcess.stderr.emit('data',"a"); - mockProcess.stderr.emit('data',"b"); - mockProcess.stdout.emit('data',"2"); - mockProcess.stdout.emit('data',"3"); - mockProcess.stderr.emit('data',"c"); - mockProcess.emit('close',123); - }) - - it("runs command and rejects on error - error", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts).then(function() { - done("Command should have rejected"); - }).catch(function(result) { - result - result.code.should.eql(456); - result.stdout.should.eql(""); - result.stderr.should.eql("test-error"); - done(); - }).catch(done); - - mockProcess.emit('error',"test-error"); - mockProcess.emit('close',456); - }) - -}); diff --git a/test/unit/@node-red/util/lib/hooks_spec.js b/test/unit/@node-red/util/lib/hooks_spec.js deleted file mode 100644 index 4b25f33d2..000000000 --- a/test/unit/@node-red/util/lib/hooks_spec.js +++ /dev/null @@ -1,338 +0,0 @@ -const should = require("should"); -const NR_TEST_UTILS = require("nr-test-utils"); - -const hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); - -describe("util/hooks", function() { - afterEach(function() { - hooks.clear(); - }) - it("allows a hook to be registered", function(done) { - let calledWith = null; - hooks.has("onSend").should.be.false(); - hooks.add("onSend", function(payload) { calledWith = payload } ) - hooks.has("onSend").should.be.true(); - let data = { a: 1 }; - hooks.trigger("onSend",data,err => { - calledWith.should.equal(data); - done(err); - }) - }) - it("rejects invalid hook id", function(done) { - try { - hooks.add("foo", function(payload) {}) - done(new Error("Invalid hook accepted")) - } catch(err) { - done(); - } - }) - it("calls hooks in the order they were registered", function(done) { - hooks.add("onSend", function(payload) { payload.order.push("A") } ) - hooks.add("onSend", function(payload) { payload.order.push("B") } ) - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - done(err); - }) - }) - - it("does not allow multiple hooks with same id.label", function() { - hooks.has("onSend.one").should.be.false(); - hooks.has("onSend").should.be.false(); - hooks.add("onSend.one", function(payload) { payload.order.push("A") } ); - hooks.has("onSend.one").should.be.true(); - hooks.has("onSend").should.be.true(); - (function() { - hooks.add("onSend.one", function(payload) { payload.order.push("B") } ) - }).should.throw(); - }) - - it("removes labelled hook", function(done) { - hooks.has("onSend.A").should.be.false(); - hooks.has("onSend.B").should.be.false(); - hooks.has("onSend").should.be.false(); - - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - - hooks.has("onSend.A").should.be.true(); - hooks.has("onSend.B").should.be.false(); - hooks.has("onSend").should.be.true(); - - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - hooks.has("onSend.A").should.be.true(); - hooks.has("onSend.B").should.be.true(); - hooks.has("onSend").should.be.true(); - - hooks.remove("onSend.A"); - - hooks.has("onSend.A").should.be.false(); - hooks.has("onSend.B").should.be.true(); - hooks.has("onSend").should.be.true(); - - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - try { - data.order.should.eql(["B"]) - - hooks.remove("onSend.B"); - - hooks.has("onSend.A").should.be.false(); - hooks.has("onSend.B").should.be.false(); - hooks.has("onSend").should.be.false(); - - done(err); - } catch(err2) { - done(err2); - } - }) - }) - - it("cannot remove unlabelled hook", function() { - hooks.add("onSend", function(payload) { payload.order.push("A") } ); - (function() { - hooks.remove("onSend") - }).should.throw(); - }) - it("removes all hooks with same label", function(done) { - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - hooks.add("preRoute.A", function(payload) { payload.order.push("C") } ) - hooks.add("preRoute.B", function(payload) { payload.order.push("D") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - hooks.trigger("preRoute", data, err => { - data.order.should.eql(["A","B","C","D"]) - - data.order = []; - - hooks.remove("*.A"); - - hooks.trigger("onSend",data,err => { - data.order.should.eql(["B"]) - hooks.trigger("preRoute", data, err => { - data.order.should.eql(["B","D"]) - }) - done(err); - }) - }) - }) - }) - it("allows a hook to remove itself whilst being called", function(done) { - let data = { order: [] } - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - hooks.add("onSend.B", function(payload) { - hooks.remove("*.B"); - }) - hooks.add("onSend.C", function(payload) { payload.order.push("C") } ) - hooks.add("onSend.D", function(payload) { payload.order.push("D") } ) - - hooks.trigger("onSend", data, err => { - try { - should.not.exist(err); - data.order.should.eql(["A","C","D"]) - done(); - } catch(e) { - done(e); - } - }) - }); - - it("allows a hook to remove itself and others whilst being called", function(done) { - let data = { order: [] } - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - hooks.add("onSend.B", function(payload) { - hooks.remove("*.B"); - hooks.remove("*.C"); - }) - hooks.add("onSend.C", function(payload) { payload.order.push("C") } ) - hooks.add("onSend.D", function(payload) { payload.order.push("D") } ) - - hooks.trigger("onSend", data, err => { - try { - should.not.exist(err); - data.order.should.eql(["A","D"]) - done(); - } catch(e) { - done(e); - } - }) - }); - - it("halts execution on return false", function(done) { - hooks.add("onSend.A", function(payload) { payload.order.push("A"); return false } ) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - err.should.be.false(); - done(); - }) - }) - it("halts execution on thrown error", function(done) { - hooks.add("onSend.A", function(payload) { payload.order.push("A"); throw new Error("error") } ) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - should.exist(err); - err.should.not.be.false() - done(); - }) - }) - - it("handler can use callback function", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done() - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - done(err); - }) - }) - - it("handler can use callback function - halt execution", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done(false) - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - err.should.be.false() - done(); - }) - }) - it("handler can use callback function - halt on error", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - done(new Error("test error")) - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql([]) - should.exist(err); - err.should.not.be.false() - done(); - }) - }) - - it("handler be an async function", function(done) { - hooks.add("onSend.A", async function(payload) { - return new Promise(resolve => { - setTimeout(function() { - payload.order.push("A") - resolve() - },30) - }); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - done(err); - }) - }) - - it("handler be an async function - halt execution", function(done) { - hooks.add("onSend.A", async function(payload) { - return new Promise(resolve => { - setTimeout(function() { - payload.order.push("A") - resolve(false) - },30) - }); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - done(err); - }) - }) - it("handler be an async function - halt on error", function(done) { - hooks.add("onSend.A", async function(payload) { - return new Promise((resolve,reject) => { - setTimeout(function() { - reject(new Error("test error")) - },30) - }); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql([]) - should.exist(err); - err.should.not.be.false() - done(); - }) - }) - - - it("handler can use callback function - promise API", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done() - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data).then(() => { - data.order.should.eql(["A","B"]) - done() - }).catch(done) - }) - - it("handler can halt execution - promise API", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done(false) - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data).then(() => { - data.order.should.eql(["A"]) - done() - }).catch(done) - }) - - it("handler can halt execution on error - promise API", function(done) { - hooks.add("onSend.A", function(payload, done) { - throw new Error("error"); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data).then(() => { - done("hooks.trigger resolved unexpectedly") - }).catch(err => { - done(); - }) - }) -}); diff --git a/test/unit/@node-red/util/lib/i18n_spec.js b/test/unit/@node-red/util/lib/i18n_spec.js deleted file mode 100644 index 5deaba032..000000000 --- a/test/unit/@node-red/util/lib/i18n_spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - - - var NR_TEST_UTILS = require("nr-test-utils"); - - var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - - -describe("@node-red/util/i18n", function() { - it.skip('more tests needed', function(){}) -}); diff --git a/test/unit/@node-red/util/lib/index_spec.js b/test/unit/@node-red/util/lib/index_spec.js deleted file mode 100644 index 3cf2347c1..000000000 --- a/test/unit/@node-red/util/lib/index_spec.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -describe("@node-red/util", function() { - it.skip('more tests needed', function(){}) -}); diff --git a/test/unit/@node-red/util/lib/log_spec.js b/test/unit/@node-red/util/lib/log_spec.js deleted file mode 100644 index 056f37672..000000000 --- a/test/unit/@node-red/util/lib/log_spec.js +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; - - -describe("@node-red/util/log", function() { - beforeEach(function () { - var spy = sinon.stub(util, 'log').callsFake(function(arg){}); - var settings = {logging: { console: { level: 'metric', metrics: true } } }; - log.init(settings); - }); - - afterEach(function() { - util.log.restore(); - }); - - it('it can raise an error', function() { - var ret = log.error("This is an error"); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - }); - - it('it can raise a trace', function() { - var ret = log.trace("This is a trace"); - sinon.assert.calledWithMatch(util.log,"[trace] This is a trace"); - }); - - it('it can raise a debug', function() { - var ret = log.debug("This is a debug"); - sinon.assert.calledWithMatch(util.log,"[debug] This is a debug"); - }); - - it('it can raise a info', function() { - var ret = log.info("This is an info"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - }); - - it('it can raise a warn', function() { - var ret = log.warn("This is a warn"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - }); - - it('it can raise a metric', function() { - var metrics = {}; - metrics.level = log.METRIC; - metrics.nodeid = "testid"; - metrics.event = "node.test.testevent"; - metrics.msgid = "12345"; - metrics.value = "the metric payload"; - var ret = log.log(metrics); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[metric] ").should.equal(0); - var body = JSON.parse(util.log.firstCall.args[0].substring(9)); - body.should.have.a.property("nodeid","testid"); - body.should.have.a.property("event","node.test.testevent"); - body.should.have.a.property("msgid","12345"); - body.should.have.a.property("value","the metric payload"); - body.should.have.a.property("timestamp"); - body.should.have.a.property("level",log.METRIC); - }); - - it('it checks metrics are enabled', function() { - log.metric().should.equal(true); - var sett = {logging: { console: { level: 'info', metrics: false } } }; - log.init(sett); - log.metric().should.equal(false); - }); - - it('it logs node type and name if provided',function() { - log.log({level:log.INFO,type:"nodeType",msg:"test",name:"nodeName",id:"nodeId"}); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[nodeType:nodeName]").should.not.equal(-1); - }); - it('it logs node type and id if no name provided',function() { - log.log({level:log.INFO,type:"nodeType",msg:"test",id:"nodeId"}); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[nodeType:nodeId]").should.not.equal(-1); - }); - - it('ignores lower level messages and metrics', function() { - var settings = {logging: { console: { level: 'warn', metrics: false } } }; - log.init(settings); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.neverCalledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); - }); - it('ignores lower level messages but accepts metrics', function() { - var settings = {logging: { console: { level: 'log', metrics: true } } }; - log.init(settings); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.calledWithMatch(util.log,"[metric] "); - }); - - it('default settings set to INFO and metrics off', function() { - log.init({logging:{}}); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); - }); - it('no logger used if custom logger handler does not exist', function() { - var settings = {logging: { customLogger: { level: 'trace', metrics: true } } }; - log.init(settings); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.neverCalledWithMatch(util.log,"[error] This is an error"); - sinon.assert.neverCalledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.neverCalledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); - }); - - it('add a custom log handler directly', function() { - var settings = {}; - log.init(settings); - - var logEvents = []; - var loggerOne = { - emit: function(event,msg) { - logEvents.push({logger:1,msg:msg}); - } - }; - var loggerTwo = { - emit: function(event,msg) { - logEvents.push({logger:2,msg:msg}); - } - }; - log.addHandler(loggerOne); - log.addHandler(loggerTwo); - - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(6); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(6); - }); - - it('remove a custom log handler directly', function() { - var settings = {}; - log.init(settings); - - var logEvents = []; - var loggerOne = { - emit: function(event,msg) { - logEvents.push({logger:1,msg:msg}); - } - }; - var loggerTwo = { - emit: function(event,msg) { - logEvents.push({logger:2,msg:msg}); - } - }; - log.addHandler(loggerOne); - log.addHandler(loggerTwo); - - log.info("This is an info"); - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(1); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(1); - - - log.removeHandler(loggerTwo); - log.info("This is an info"); - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(2); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(1); - - log.removeHandler(loggerOne); - log.info("This is an info"); - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(2); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(1); - - - }); - it('it can log without exception', function() { - var msg = { - msg: { - mystrangeobj:"hello", - }, - }; - msg.msg.toString = function(){ - throw new Error('Exception in toString - should have been caught'); - } - msg.msg.constructor = { name: "strangeobj" }; - var ret = log.info(msg.msg); - }); - it('it can log an object but use .message', function() { - var msg = { - msg: { - message: "my special message", - mystrangeobj:"hello", - }, - }; - var ret = log.info(msg.msg); - sinon.assert.calledWithMatch(util.log,"my special message"); - }); - - - -}); diff --git a/test/unit/@node-red/util/lib/util_spec.js b/test/unit/@node-red/util/lib/util_spec.js deleted file mode 100644 index 5007fe5c5..000000000 --- a/test/unit/@node-red/util/lib/util_spec.js +++ /dev/null @@ -1,1092 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var util = NR_TEST_UTILS.require("@node-red/util").util; - -describe("@node-red/util/util", function() { - describe('generateId', function() { - it('generates an id', function() { - var id = util.generateId(); - var id2 = util.generateId(); - id.should.not.eql(id2); - }); - }); - describe('compareObjects', function() { - it('numbers', function() { - util.compareObjects(0,0).should.equal(true); - util.compareObjects(0,1).should.equal(false); - util.compareObjects(1000,1001).should.equal(false); - util.compareObjects(1000,1000).should.equal(true); - util.compareObjects(0,"0").should.equal(false); - util.compareObjects(1,"1").should.equal(false); - util.compareObjects(0,null).should.equal(false); - util.compareObjects(0,undefined).should.equal(false); - }); - it('strings', function() { - util.compareObjects("","").should.equal(true); - util.compareObjects("a","a").should.equal(true); - util.compareObjects("",null).should.equal(false); - util.compareObjects("",undefined).should.equal(false); - }); - - it('arrays', function() { - util.compareObjects(["a"],["a"]).should.equal(true); - util.compareObjects(["a"],["a","b"]).should.equal(false); - util.compareObjects(["a","b"],["b"]).should.equal(false); - util.compareObjects(["a"],"a").should.equal(false); - util.compareObjects([[1],["a"]],[[1],["a"]]).should.equal(true); - util.compareObjects([[1],["a"]],[["a"],[1]]).should.equal(false); - }); - it('objects', function() { - util.compareObjects({"a":1},{"a":1,"b":1}).should.equal(false); - util.compareObjects({"a":1,"b":1},{"a":1,"b":1}).should.equal(true); - util.compareObjects({"b":1,"a":1},{"a":1,"b":1}).should.equal(true); - }); - it('Buffer', function() { - util.compareObjects(Buffer.from("hello"),Buffer.from("hello")).should.equal(true); - util.compareObjects(Buffer.from("hello"),Buffer.from("hello ")).should.equal(false); - util.compareObjects(Buffer.from("hello"),"hello").should.equal(false); - }); - - }); - - describe('ensureString', function() { - it('strings are preserved', function() { - util.ensureString('string').should.equal('string'); - }); - it('Buffer is converted', function() { - var s = util.ensureString(Buffer.from('foo')); - s.should.equal('foo'); - (typeof s).should.equal('string'); - }); - it('Object is converted to JSON', function() { - var s = util.ensureString({foo: "bar"}); - (typeof s).should.equal('string'); - should.deepEqual(JSON.parse(s), {foo:"bar"}); - }); - it('stringifies other things', function() { - var s = util.ensureString(123); - (typeof s).should.equal('string'); - s.should.equal('123'); - }); - }); - - describe('ensureBuffer', function() { - it('Buffers are preserved', function() { - var b = Buffer.from(''); - util.ensureBuffer(b).should.equal(b); - }); - it('string is converted', function() { - var b = util.ensureBuffer('foo'); - var expected = Buffer.from('foo'); - for (var i = 0; i < expected.length; i++) { - b[i].should.equal(expected[i]); - } - Buffer.isBuffer(b).should.equal(true); - }); - it('Object is converted to JSON', function() { - var obj = {foo: "bar"} - var b = util.ensureBuffer(obj); - Buffer.isBuffer(b).should.equal(true); - should.deepEqual(JSON.parse(b), obj); - }); - it('stringifies other things', function() { - var b = util.ensureBuffer(123); - Buffer.isBuffer(b).should.equal(true); - var expected = Buffer.from('123'); - for (var i = 0; i < expected.length; i++) { - b[i].should.equal(expected[i]); - } - }); - }); - - describe('cloneMessage', function() { - it('clones a simple message', function() { - var msg = {string:"hi",array:[1,2,3],object:{a:1,subobject:{b:2}}}; - - var cloned = util.cloneMessage(msg); - - cloned.should.eql(msg); - - cloned.should.not.equal(msg); - cloned.array.should.not.equal(msg.string); - cloned.object.should.not.equal(msg.object); - cloned.object.subobject.should.not.equal(msg.object.subobject); - - cloned.should.not.have.property("req"); - cloned.should.not.have.property("res"); - }); - it('does not clone http req/res properties', function() { - var msg = {req:{a:1},res:{b:2}}; - - var cloned = util.cloneMessage(msg); - - cloned.should.eql(msg); - cloned.should.not.equal(msg); - - cloned.req.should.equal(msg.req); - cloned.res.should.equal(msg.res); - }); - it('handles undefined values without throwing an error', function() { - var result = util.cloneMessage(undefined); - should.not.exist(result); - }) - }); - describe('getObjectProperty', function() { - it('gets a property beginning with "msg."', function() { - // getMessageProperty strips off `msg.` prefixes. - // getObjectProperty does not - var obj = { msg: { a: "foo"}, a: "bar"}; - var v = util.getObjectProperty(obj,"msg.a"); - v.should.eql("foo"); - }) - }); - describe('getMessageProperty', function() { - it('retrieves a simple property', function() { - var v = util.getMessageProperty({a:"foo"},"msg.a"); - v.should.eql("foo"); - var v2 = util.getMessageProperty({a:"foo"},"a"); - v2.should.eql("foo"); - }); - it('retrieves a nested property', function() { - var v = util.getMessageProperty({a:"foo",b:{foo:1,bar:2}},"msg.b[msg.a]"); - v.should.eql(1); - var v2 = util.getMessageProperty({a:"bar",b:{foo:1,bar:2}},"b[msg.a]"); - v2.should.eql(2); - }); - - it('should return undefined if property does not exist', function() { - var v = util.getMessageProperty({a:"foo"},"msg.b"); - should.not.exist(v); - }); - it('should throw error if property parent does not exist', function() { - /*jshint immed: false */ - (function() { - util.getMessageProperty({a:"foo"},"msg.a.b.c"); - }).should.throw(); - }); - it('retrieves a property with array syntax', function() { - var v = util.getMessageProperty({a:["foo","bar"]},"msg.a[0]"); - v.should.eql("foo"); - var v2 = util.getMessageProperty({a:[null,{b:"foo"}]},"a[1].b"); - v2.should.eql("foo"); - var v3 = util.getMessageProperty({a:[[["foo"]]]},"a[0][0][0]"); - v3.should.eql("foo"); - }); - - }); - describe('setObjectProperty', function() { - it('set a property beginning with "msg."', function() { - // setMessageProperty strips off `msg.` prefixes. - // setObjectProperty does not - var obj = {}; - var result = util.setObjectProperty(obj,"msg.a","bar"); - result.should.be.true(); - obj.should.have.property("msg"); - obj.msg.should.have.property("a","bar"); - }) - }); - describe('setMessageProperty', function() { - it('sets a property', function() { - var msg = {a:"foo"}; - var result = util.setMessageProperty(msg,"msg.a","bar"); - result.should.be.true(); - msg.a.should.eql('bar'); - }); - it('sets a deep level property', function() { - var msg = {a:{b:{c:"foo"}}}; - var result = util.setMessageProperty(msg,"msg.a.b.c","bar"); - result.should.be.true(); - msg.a.b.c.should.eql('bar'); - }); - it('creates missing parent properties by default', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b.c","bar"); - result.should.be.true(); - msg.a.b.c.should.eql('bar'); - }) - it('does not create missing parent properties', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b.c","bar",false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not create missing parent properties of array', function () { - var msg = {a:{}}; - var result = util.setMessageProperty(msg, "msg.a.b[1].c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - it('does not create missing parent properties of string', function() { - var msg = {a:"foo"}; - var result = util.setMessageProperty(msg, "msg.a.b.c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not set property of existing string property', function() { - var msg = {a:"foo"}; - var result = util.setMessageProperty(msg, "msg.a.b", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - it('does not set property of existing number property', function() { - var msg = {a:123}; - var result = util.setMessageProperty(msg, "msg.a.b", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not create missing parent properties of number', function() { - var msg = {a:123}; - var result = util.setMessageProperty(msg, "msg.a.b.c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - it('does not set property of existing boolean property', function() { - var msg = {a:true}; - var result = util.setMessageProperty(msg, "msg.a.b", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not create missing parent properties of boolean', function() { - var msg = {a:true}; - var result = util.setMessageProperty(msg, "msg.a.b.c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - - it('deletes property if value is undefined', function() { - var msg = {a:{b:{c:"foo"}}}; - var result = util.setMessageProperty(msg,"msg.a.b.c",undefined); - result.should.be.true(); - should.not.exist(msg.a.b.c); - }) - it('does not create missing parent properties if value is undefined', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b.c",undefined); - result.should.be.false(); - should.not.exist(msg.a.b); - }); - it('sets a property with array syntax', function() { - var msg = {a:{b:["foo",{c:["",""]}]}}; - var result = util.setMessageProperty(msg,"msg.a.b[1].c[1]","bar"); - result.should.be.true(); - msg.a.b[1].c[1].should.eql('bar'); - }); - it('creates missing array elements - final property', function() { - var msg = {a:[]}; - var result = util.setMessageProperty(msg,"msg.a[2]","bar"); - result.should.be.true(); - msg.a.should.have.length(3); - msg.a[2].should.eql("bar"); - }); - it('creates missing array elements - mid property', function() { - var msg = {}; - var result = util.setMessageProperty(msg,"msg.a[2].b","bar"); - result.should.be.true(); - msg.a.should.have.length(3); - msg.a[2].b.should.eql("bar"); - }); - it('creates missing array elements - multi-arrays', function() { - var msg = {}; - var result = util.setMessageProperty(msg,"msg.a[2][2]","bar"); - result.should.be.true(); - msg.a.should.have.length(3); - msg.a.should.be.instanceOf(Array); - msg.a[2].should.have.length(3); - msg.a[2].should.be.instanceOf(Array); - msg.a[2][2].should.eql("bar"); - }); - it('does not create missing array elements - mid property', function () { - var msg = {a:[]}; - var result = util.setMessageProperty(msg, "msg.a[1][1]", "bar", false); - result.should.be.false(); - msg.a.should.empty(); - }); - it('does not create missing array elements - final property', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b[2]","bar",false); - result.should.be.false(); - should.not.exist(msg.a.b); - // check it has not been misinterpreted - msg.a.should.not.have.property("b[2]"); - }); - it('deletes property inside array if value is undefined', function() { - var msg = {a:[1,2,3]}; - var result = util.setMessageProperty(msg,"msg.a[1]",undefined); - result.should.be.true(); - msg.a.should.have.length(2); - msg.a[0].should.eql(1); - msg.a[1].should.eql(3); - }) - it('handles nested message property references', function() { - var obj = {a:"foo",b:{}}; - var result = util.setObjectProperty(obj,"b[msg.a]","bar"); - result.should.be.true(); - obj.b.should.have.property("foo","bar"); - }); - it('handles nested message property references', function() { - var obj = {a:"foo",b:{"foo":[0,0,0]}}; - var result = util.setObjectProperty(obj,"b[msg.a][2]","bar"); - result.should.be.true(); - obj.b.foo.should.eql([0,0,"bar"]) - }); - }); - - describe('evaluateNodeProperty', function() { - it('returns string',function() { - var result = util.evaluateNodeProperty('hello','str'); - result.should.eql('hello'); - }); - it('returns number',function() { - var result = util.evaluateNodeProperty('0123','num'); - result.should.eql(123); - }); - it('returns evaluated json',function() { - var result = util.evaluateNodeProperty('{"a":123}','json'); - result.should.eql({a:123}); - }); - it('returns regex',function() { - var result = util.evaluateNodeProperty('^abc$','re'); - result.toString().should.eql("/^abc$/"); - }); - it('returns boolean',function() { - var result = util.evaluateNodeProperty('true','bool'); - result.should.be.true(); - result = util.evaluateNodeProperty('TrUe','bool'); - result.should.be.true(); - result = util.evaluateNodeProperty('false','bool'); - result.should.be.false(); - result = util.evaluateNodeProperty('','bool'); - result.should.be.false(); - }); - it('returns date',function() { - var result = util.evaluateNodeProperty('','date'); - (Date.now() - result).should.be.approximately(0,50); - }); - it('returns bin', function () { - var result = util.evaluateNodeProperty('[1, 2]','bin'); - result[0].should.eql(1); - result[1].should.eql(2); - }); - it('returns msg property',function() { - var result = util.evaluateNodeProperty('foo.bar','msg',{},{foo:{bar:"123"}}); - result.should.eql("123"); - }); - it('throws an error if callback is not defined', function (done) { - try { - util.evaluateNodeProperty(' ','msg',{},{foo:{bar:"123"}}); - done("should throw an error"); - } catch (err) { - done(); - } - }); - it('returns flow property',function() { - var result = util.evaluateNodeProperty('foo.bar','flow',{ - context:function() { return { - flow: { get: function(k) { - if (k === 'foo.bar') { - return '123'; - } else { - return null; - } - }} - }} - },{}); - result.should.eql("123"); - }); - it('returns global property',function() { - var result = util.evaluateNodeProperty('foo.bar','global',{ - context:function() { return { - global: { get: function(k) { - if (k === 'foo.bar') { - return '123'; - } else { - return null; - } - }} - }} - },{}); - result.should.eql("123"); - }); - it('returns jsonata result', function () { - var result = util.evaluateNodeProperty('$abs(-1)','jsonata',{},{}); - result.should.eql(1); - }); - it('returns null', function() { - var result = util.evaluateNodeProperty(null,'null'); - (result === null).should.be.true(); - }) - describe('environment variable', function() { - before(function() { - process.env.NR_TEST_A = "foo"; - process.env.NR_TEST_B = "${NR_TEST_A}"; - }) - after(function() { - delete process.env.NR_TEST_A; - delete process.env.NR_TEST_B; - }) - - it('returns an environment variable - NR_TEST_A', function() { - var result = util.evaluateNodeProperty('NR_TEST_A','env'); - result.should.eql('foo'); - }); - it('returns an environment variable - ${NR_TEST_A}', function() { - var result = util.evaluateNodeProperty('${NR_TEST_A}','env'); - result.should.eql('foo'); - }); - it('returns an environment variable - ${NR_TEST_A', function() { - var result = util.evaluateNodeProperty('${NR_TEST_A','env'); - result.should.eql(''); - }); - it('returns an environment variable - foo${NR_TEST_A}bar', function() { - var result = util.evaluateNodeProperty('123${NR_TEST_A}456','env'); - result.should.eql('123foo456'); - }); - it('returns an environment variable - foo${NR_TEST_B}bar', function() { - var result = util.evaluateNodeProperty('123${NR_TEST_B}456','env'); - result.should.eql('123${NR_TEST_A}456'); - }); - - }); - }); - - describe('normalisePropertyExpression', function() { - function testABC(input,expected) { - var result = util.normalisePropertyExpression(input); - // console.log("+",input); - // console.log(result); - result.should.eql(expected); - } - function testABCWithMessage(input,msg,expected) { - var result = util.normalisePropertyExpression(input,msg); - // console.log("+",input); - // console.log(result); - result.should.eql(expected); - } - function testInvalid(input,msg) { - /*jshint immed: false */ - (function() { - util.normalisePropertyExpression(input,msg); - }).should.throw(); - } - function testToString(input,msg,expected) { - var result = util.normalisePropertyExpression(input,msg,true); - console.log("+",input); - console.log(result); - result.should.eql(expected); - } - it('pass a.b.c',function() { testABC('a.b.c',['a','b','c']); }) - it('pass a["b"]["c"]',function() { testABC('a["b"]["c"]',['a','b','c']); }) - it('pass a["b"].c',function() { testABC('a["b"].c',['a','b','c']); }) - it("pass a['b'].c",function() { testABC("a['b'].c",['a','b','c']); }) - - it("pass a[0].c",function() { testABC("a[0].c",['a',0,'c']); }) - it("pass a.0.c",function() { testABC("a.0.c",['a',0,'c']); }) - it("pass a['a.b[0]'].c",function() { testABC("a['a.b[0]'].c",['a','a.b[0]','c']); }) - it("pass a[0][0][0]",function() { testABC("a[0][0][0]",['a',0,0,0]); }) - it("pass '1.2.3.4'",function() { testABC("'1.2.3.4'",['1.2.3.4']); }) - it("pass 'a.b'[1]",function() { testABC("'a.b'[1]",['a.b',1]); }) - it("pass 'a.b'.c",function() { testABC("'a.b'.c",['a.b','c']); }) - - it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); }) - it("pass a[msg[msg.b]]",function() { testABC("a[msg[msg.b]]",["a",["msg",["msg","b"]]]); }) - it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); }) - it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); }) - it("pass a[msg['b]\"[']]",function() { testABC("a[msg['b]\"[']]",["a",["msg","b]\"["]]); }) - it("pass a[msg['b][']]",function() { testABC("a[msg['b][']]",["a",["msg","b]["]]); }) - it("pass b[msg.a][2]",function() { testABC("b[msg.a][2]",["b",["msg","a"],2])}) - - it("pass b[msg.a][2] (with message)",function() { testABCWithMessage("b[msg.a][2]",{a: "foo"},["b","foo",2])}) - - it('pass a.$b.c',function() { testABC('a.$b.c',['a','$b','c']); }) - it('pass a["$b"].c',function() { testABC('a["$b"].c',['a','$b','c']); }) - it('pass a._b.c',function() { testABC('a._b.c',['a','_b','c']); }) - it('pass a["_b"].c',function() { testABC('a["_b"].c',['a','_b','c']); }) - - it("pass a['a.b[0]'].c",function() { testToString("a['a.b[0]'].c",null,'a["a.b[0]"]["c"]'); }) - it("pass a.b.c",function() { testToString("a.b.c",null,'a["b"]["c"]'); }) - it('pass a[msg.c][0]["fred"]',function() { testToString('a[msg.c][0]["fred"]',{c:"123"},'a["123"][0]["fred"]'); }) - - it("fail a'b'.c",function() { testInvalid("a'b'.c"); }) - it("fail a['b'.c",function() { testInvalid("a['b'.c"); }) - it("fail a[]",function() { testInvalid("a[]"); }) - it("fail a]",function() { testInvalid("a]"); }) - it("fail a[",function() { testInvalid("a["); }) - it("fail a[0d]",function() { testInvalid("a[0d]"); }) - it("fail a['",function() { testInvalid("a['"); }) - it("fail a[']",function() { testInvalid("a[']"); }) - it("fail a[0']",function() { testInvalid("a[0']"); }) - it("fail a.[0]",function() { testInvalid("a.[0]"); }) - it("fail [0]",function() { testInvalid("[0]"); }) - it("fail a[0",function() { testInvalid("a[0"); }) - it("fail a.",function() { testInvalid("a."); }) - it("fail .a",function() { testInvalid(".a"); }) - it("fail a. b",function() { testInvalid("a. b"); }) - it("fail a.b",function() { testInvalid(" a.b"); }) - it("fail a[0].[1]",function() { testInvalid("a[0].[1]"); }) - it("fail a['']",function() { testInvalid("a['']"); }) - it("fail 'a.b'c",function() { testInvalid("'a.b'c"); }) - it("fail ",function() { testInvalid("");}) - it("fail a[b]",function() { testInvalid("a[b]"); }) - it("fail a[msg.]",function() { testInvalid("a[msg.]"); }) - it("fail a[msg[]",function() { testInvalid("a[msg[]"); }) - it("fail a[msg.[]]",function() { testInvalid("a[msg.[]]"); }) - it("fail a[msg['af]]",function() { testInvalid("a[msg['af]]"); }) - it("fail b[msg.undefined][2] (with message)",function() { testInvalid("b[msg.undefined][2]",{})}) - - }); - - describe('normaliseNodeTypeName', function() { - function normalise(input, expected) { - var result = util.normaliseNodeTypeName(input); - result.should.eql(expected); - } - - it('pass blank',function() { normalise("", "") }); - it('pass ab1',function() { normalise("ab1", "ab1") }); - it('pass AB1',function() { normalise("AB1", "aB1") }); - it('pass a b 1',function() { normalise("a b 1", "aB1") }); - it('pass a-b-1',function() { normalise("a-b-1", "aB1") }); - it('pass ab1 ',function() { normalise(" ab1 ", "ab1") }); - it('pass _a_b_1_',function() { normalise("_a_b_1_", "aB1") }); - it('pass http request',function() { normalise("http request", "httpRequest") }); - it('pass HttpRequest',function() { normalise("HttpRequest", "httpRequest") }); - }); - - describe('prepareJSONataExpression', function() { - it('prepares an expression', function() { - var result = util.prepareJSONataExpression('payload',{}); - result.should.have.property('evaluate'); - result.should.have.property('assign'); - result.should.have.property('_legacyMode', false); - }); - it('prepares a legacyMode expression', function() { - var result = util.prepareJSONataExpression('msg.payload',{}); - result.should.have.property('evaluate'); - result.should.have.property('assign'); - result.should.have.property('_legacyMode', true); - }); - }); - describe('evaluateJSONataExpression', function() { - it('evaluates an expression', function() { - var expr = util.prepareJSONataExpression('payload',{}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - result.should.eql("hello"); - }); - it('evaluates a legacyMode expression', function() { - var expr = util.prepareJSONataExpression('msg.payload',{}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - result.should.eql("hello"); - }); - it('accesses flow context from an expression', function() { - var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - result.should.eql("bar"); - }); - it('accesses undefined environment variable from an expression', function() { - var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql(''); - }); - it('accesses environment variable from an expression', function() { - process.env.UTIL_ENV = 'foo'; - var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql('foo'); - }); - it('accesses moment from an expression', function() { - var expr = util.prepareJSONataExpression('$moment("2020-05-27", "YYYY-MM-DD").add(7, "days").add(1, "months").format("YYYY-MM-DD")',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql('2020-07-03'); - }); - it('accesses moment-timezone from an expression', function() { - var expr = util.prepareJSONataExpression('$moment("2013-11-18 11:55Z").tz("Asia/Taipei").format()',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql('2013-11-18T19:55:00+08:00'); - }); - it('handles non-existant flow context variable', function() { - var expr = util.prepareJSONataExpression('$flowContext("nonExistant")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - should.not.exist(result); - }); - it('handles non-existant global context variable', function() { - var expr = util.prepareJSONataExpression('$globalContext("nonExistant")',{context:function() { return {global:{get: function(key) { return {'foo':'bar'}[key]}}}}}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - should.not.exist(result); - }); - it('handles async flow context access', function(done) { - var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key,store,callback) { setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('handles async global context access', function(done) { - var expr = util.prepareJSONataExpression('$globalContext("foo")',{context:function() { return {global:{get: function(key,store,callback) { setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('handles persistable store in flow context access', function(done) { - var storeName; - var expr = util.prepareJSONataExpression('$flowContext("foo", "flowStoreName")',{context:function() { return {flow:{get: function(key,store,callback) { storeName = store;setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - storeName.should.equal("flowStoreName"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('handles persistable store in global context access', function(done) { - var storeName; - var expr = util.prepareJSONataExpression('$globalContext("foo", "globalStoreName")',{context:function() { return {global:{get: function(key,store,callback) { storeName = store;setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - storeName.should.equal("globalStoreName"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('callbacks with error when invalid expression was specified', function (done) { - var expr = util.prepareJSONataExpression('$abc(1)',{}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value){ - should.exist(err); - done(); - }); - }); - }); - - describe('encodeObject', function () { - it('encodes Error with message', function() { - var err = new Error("encode error"); - err.name = 'encodeError'; - var msg = {msg:err}; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var resultJson = JSON.parse(result.msg); - resultJson.name.should.eql('encodeError'); - resultJson.message.should.eql('encode error'); - }); - it('encodes Error without message', function() { - var err = new Error(); - err.name = 'encodeError'; - err.toString = function(){return 'error message';} - var msg = {msg:err}; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var resultJson = JSON.parse(result.msg); - resultJson.name.should.eql('encodeError'); - resultJson.message.should.eql('error message'); - }); - it('encodes Buffer', function() { - var msg = {msg:Buffer.from("abc")}; - var result = util.encodeObject(msg,{maxLength:4}); - result.format.should.eql("buffer[3]"); - result.msg[0].should.eql('6'); - result.msg[1].should.eql('1'); - result.msg[2].should.eql('6'); - result.msg[3].should.eql('2'); - }); - it('encodes function', function() { - var msg = {msg:function(){}}; - var result = util.encodeObject(msg); - result.format.should.eql("function"); - result.msg.should.eql('[function]'); - }); - it('encodes boolean', function() { - var msg = {msg:true}; - var result = util.encodeObject(msg); - result.format.should.eql("boolean"); - result.msg.should.eql('true'); - }); - it('encodes number', function() { - var msg = {msg:123}; - var result = util.encodeObject(msg); - result.format.should.eql("number"); - result.msg.should.eql('123'); - }); - it('encodes 0', function() { - var msg = {msg:0}; - var result = util.encodeObject(msg); - result.format.should.eql("number"); - result.msg.should.eql('0'); - }); - it('encodes null', function() { - var msg = {msg:null}; - var result = util.encodeObject(msg); - result.format.should.eql("null"); - result.msg.should.eql('(undefined)'); - }); - it('encodes undefined', function() { - var msg = {msg:undefined}; - var result = util.encodeObject(msg); - result.format.should.eql("undefined"); - result.msg.should.eql('(undefined)'); - }); - it('encodes string', function() { - var msg = {msg:'1234567890'}; - var result = util.encodeObject(msg,{maxLength:6}); - result.format.should.eql("string[10]"); - result.msg.should.eql('123456...'); - }); - - it('encodes Map', function() { - const m = new Map(); - m.set("a",1); - m.set("b",2); - var msg = {msg:m}; - var result = util.encodeObject(msg); - result.format.should.eql("map"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("__enc__",true); - resultJson.should.have.property("type","map"); - resultJson.should.have.property("data",{"a":1,"b":2}); - resultJson.should.have.property("length",2) - }); - - it('encodes Set', function() { - const m = new Set(); - m.add("a"); - m.add("b"); - var msg = {msg:m}; - var result = util.encodeObject(msg); - result.format.should.eql("set[2]"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("__enc__",true); - resultJson.should.have.property("type","set"); - resultJson.should.have.property("data",["a","b"]); - resultJson.should.have.property("length",2) - }); - - - describe('encode object', function() { - it('object', function() { - var msg = { msg:{"foo":"bar"} }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.foo.should.eql('bar'); - }); - it('object whose name includes error', function() { - function MyErrorObj(){ - this.name = 'my error obj'; - this.message = 'my error message'; - }; - var msg = { msg:new MyErrorObj() }; - var result = util.encodeObject(msg); - result.format.should.eql("MyErrorObj"); - var resultJson = JSON.parse(result.msg); - resultJson.name.should.eql('my error obj'); - resultJson.message.should.eql('my error message'); - }); - - it('object with undefined property', function() { - var msg = { msg:{a:1,b:undefined,c:3 } }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("a",1); - resultJson.should.have.property("c",3); - resultJson.should.have.property("b"); - resultJson.b.should.have.property("__enc__", true); - resultJson.b.should.have.property("type", "undefined"); - }); - it('object with Map property', function() { - const m = new Map(); - m.set("a",1); - m.set("b",2); - var msg = {msg:{"aMap":m}}; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("aMap"); - resultJson.aMap.should.have.property("__enc__",true); - resultJson.aMap.should.have.property("type","map"); - resultJson.aMap.should.have.property("data",{"a":1,"b":2}); - resultJson.aMap.should.have.property("length",2) - }); - it('object with Set property', function() { - const m = new Set(); - m.add("a"); - m.add("b"); - var msg = {msg:{"aSet":m}}; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("aSet"); - resultJson.aSet.should.have.property("__enc__",true); - resultJson.aSet.should.have.property("type","set"); - resultJson.aSet.should.have.property("data",["a","b"]); - resultJson.aSet.should.have.property("length",2) - }); - it('constructor of IncomingMessage', function() { - function IncomingMessage(){}; - var msg = { msg:new IncomingMessage() }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.empty(); - }); - it('_req key in msg', function() { - function Socket(){}; - var msg = { msg:{"_req":123} }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson._req.__enc__.should.eql(true); - resultJson._req.type.should.eql('internal'); - }); - it('_res key in msg', function() { - function Socket(){}; - var msg = { msg:{"_res":123} }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson._res.__enc__.should.eql(true); - resultJson._res.type.should.eql('internal'); - }); - it('array of error', function() { - var msg = { msg:[new Error("encode error")] }; - var result = util.encodeObject(msg); - result.format.should.eql("array[1]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].should.eql('Error: encode error'); - }); - it('long array in msg', function() { - var msg = {msg:{array:[1,2,3,4]}}; - var result = util.encodeObject(msg,{maxLength:2}); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.array.__enc__.should.eql(true); - resultJson.array.data[0].should.eql(1); - resultJson.array.data[1].should.eql(2); - resultJson.array.length.should.eql(4); - }); - it('array of string', function() { - var msg = { msg:["abcde","12345"] }; - var result = util.encodeObject(msg,{maxLength:3}); - result.format.should.eql("array[2]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].should.eql('abc...'); - resultJson[1].should.eql('123...'); - }); - it('array containing undefined', function() { - var msg = { msg:[1,undefined,3]}; - var result = util.encodeObject(msg); - result.format.should.eql("array[3]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].should.eql(1); - resultJson[2].should.eql(3); - resultJson[1].__enc__.should.be.true(); - resultJson[1].type.should.eql("undefined"); - }); - it('array of function', function() { - var msg = { msg:[function(){}] }; - var result = util.encodeObject(msg); - result.format.should.eql("array[1]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].__enc__.should.eql(true); - resultJson[0].type.should.eql('function'); - }); - it('array of number', function() { - var msg = { msg:[1,2,3] }; - var result = util.encodeObject(msg,{maxLength:2}); - result.format.should.eql("array[3]"); - var resultJson = JSON.parse(result.msg); - resultJson.__enc__.should.eql(true); - resultJson.data[0].should.eql(1); - resultJson.data[1].should.eql(2); - resultJson.data.length.should.eql(2); - resultJson.length.should.eql(3); - }); - it('array of special number', function() { - var msg = { msg:[NaN,Infinity,-Infinity] }; - var result = util.encodeObject(msg); - result.format.should.eql("array[3]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].__enc__.should.eql(true); - resultJson[0].type.should.eql('number'); - resultJson[0].data.should.eql('NaN'); - resultJson[1].data.should.eql('Infinity'); - resultJson[2].data.should.eql('-Infinity'); - }); - it('constructor of Buffer in msg', function() { - var msg = { msg:{buffer:Buffer.from([1,2,3,4])} }; - var result = util.encodeObject(msg,{maxLength:2}); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.buffer.__enc__.should.eql(true); - resultJson.buffer.length.should.eql(4); - resultJson.buffer.data[0].should.eql(1); - resultJson.buffer.data[1].should.eql(2); - }); - it('constructor of ServerResponse', function() { - function ServerResponse(){}; - var msg = { msg: new ServerResponse() }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.eql('[internal]'); - }); - it('constructor of Socket in msg', function() { - function Socket(){}; - var msg = { msg: { socket: new Socket() } }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.socket.should.eql('[internal]'); - }); - it('object which fails to serialise', function(done) { - var msg = { - msg: { - obj:{ - cantserialise:{ - message:'this will not be displayed', - toJSON: function(val) { - throw 'this exception should have been caught'; - return 'should not display because we threw first'; - }, - }, - canserialise:{ - message:'this should be displayed', - } - }, - } - }; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var success = (result.msg.indexOf('cantserialise') > 0); - success &= (result.msg.indexOf('this exception should have been caught') > 0); - success &= (result.msg.indexOf('canserialise') > 0); - success.should.eql(1); - done(); - }); - it('object which fails to serialise - different error type', function(done) { - var msg = { - msg: { - obj:{ - cantserialise:{ - message:'this will not be displayed', - toJSON: function(val) { - throw new Error('this exception should have been caught'); - return 'should not display because we threw first'; - }, - }, - canserialise:{ - message:'this should be displayed', - } - }, - } - }; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var success = (result.msg.indexOf('cantserialise') > 0); - success &= (result.msg.indexOf('this exception should have been caught') > 0); - success &= (result.msg.indexOf('canserialise') > 0); - success.should.eql(1); - done(); - }); - it('very large object which fails to serialise should be truncated', function(done) { - var msg = { - msg: { - obj:{ - big:"", - cantserialise:{ - message:'this will not be displayed', - toJSON: function(val) { - throw new Error('this exception should have been caught'); - return 'should not display because we threw first'; - }, - }, - canserialise:{ - message:'this should be displayed', - } - }, - } - }; - - for (var i = 0; i < 1000; i++) { - msg.msg.obj.big += 'some more string '; - } - - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var resultJson = JSON.parse(result.msg); - var success = (resultJson.message.length <= 1000); - success.should.eql(true); - done(); - }); - it('test bad toString', function(done) { - var msg = { - msg: { - mystrangeobj:"hello", - }, - }; - msg.msg.toString = function(){ - throw new Error('Exception in toString - should have been caught'); - } - msg.msg.constructor = { name: "strangeobj" }; - - var result = util.encodeObject(msg); - var success = (result.msg.indexOf('[Type not printable]') >= 0); - success.should.eql(true); - done(); - }); - it('test bad object constructor', function(done) { - var msg = { - msg: { - mystrangeobj:"hello", - constructor: { - get name(){ - throw new Error('Exception in constructor name'); - } - } - }, - }; - var result = util.encodeObject(msg); - done(); - }); - - }); - }); - -}); diff --git a/test/unit/_spec.js b/test/unit/_spec.js deleted file mode 100644 index d099836f3..000000000 --- a/test/unit/_spec.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -/** - * This test simply checks that for every .js file there exists - * a *_spec.js file under ./test correspondingly. - */ - -/** - * Currently we're only checking the core components under ./red - * TODO: Increase the scope of this check - */ - -var fs = require("fs-extra"); -var should = require("should"); -var path = require('path'); - -// Directories to check with .js files and _spec.js files respectively -var rootdir = path.resolve(__dirname, "../.."); -var jsdir = path.resolve(__dirname, "../../packages/node_modules/"); -var testdir = path.resolve(__dirname); - -var walkDirectory = function(dir) { - var p = fs.readdir(dir); - var errors = []; - return p.then(function(list) { - var promises = []; - list.forEach(function(file) { - var filePath = path.join(dir,file); - - if (!/@node-red\/(editor-client|nodes)/.test(filePath) && !/node-red\/settings\.js/.test(filePath) && !/\/docs\//.test(filePath)) { - promises.push(fs.stat(filePath).then(function(stat){ - if (stat.isDirectory()) { - return walkDirectory(filePath).then(function(results) { - if (results) { - errors = errors.concat(results); - } - }); - } else if (/\.js$/.test(filePath)) { - var testFile = filePath.replace(jsdir, testdir).replace(".js", "_spec.js"); - return fs.exists(testFile).then(function(exists) { - if (!exists) { - errors.push(testFile.substring(rootdir.length+1)); - } else { - return fs.stat(testFile).then(function(stat) { - if (stat.size === 0) { - errors.push("[empty] "+testFile.substring(rootdir.length+1)); - } - }) - } - }); - } - })); - } - }); - return Promise.all(promises).then(function() { - return errors; - }) - }); -} - -describe('_spec.js', function() { - this.timeout(50000); // we might not finish within the Mocha default timeout limit, project will also grow - it('is checking if all .js files have a corresponding _spec.js test file.', function(done) { - walkDirectory(jsdir).then(function(errors) { - if (errors.length > 0) { - var error = new Error("Missing/empty _spec files:\n\t"+errors.join("\n\t")); - done(error); - } else { - done(); - } - }); - }); -}); diff --git a/test/unit/node-red/lib/red_spec.js b/test/unit/node-red/lib/red_spec.js deleted file mode 100644 index 4cd430822..000000000 --- a/test/unit/node-red/lib/red_spec.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var fs = require("fs"); -var path = require("path"); - - -var NR_TEST_UTILS = require("nr-test-utils"); - -var api = NR_TEST_UTILS.require("@node-red/runtime/lib/api"); - -var RED = NR_TEST_UTILS.require("node-red"); - -var runtime = NR_TEST_UTILS.require("@node-red/runtime"); -var api = NR_TEST_UTILS.require("@node-red/runtime/lib/api"); - - -describe("red/red", function() { - - // describe("check build", function() { - // beforeEach(function() { - // sinon.stub(runtime,"init").callsFake(function() {}); - // sinon.stub(api,"init").callsFake(function() {}); - // // sinon.stub(RED,"version").callsFake(function() { return "version";}); - // }); - // afterEach(function() { - // runtime.init.restore(); - // api.init.restore(); - // fs.statSync.restore(); - // // RED.version.restore(); - // }); - // it.skip('warns if build has not been run',function() { - // sinon.stub(fs,"statSync").callsFake(function() { throw new Error();}); - // - // /*jshint immed: false */ - // (function() { - // RED.init({},{}); - // }).should.throw("Node-RED not built"); - // }); - // it('passed if build has been run',function() { - // sinon.stub(fs,"statSync").callsFake(function() { }); - // RED.init({},{}); - // }); - // }); - - describe("externals", function() { - it('reports version', function() { - /\d+\.\d+\.\d+(-git)?/.test(RED.version()).should.be.true(); - }); - it.skip('access server externals', function() { - // TODO: unstubable accessors - need to make this testable - // RED.app; - // RED.httpAdmin; - // RED.httpNode; - // RED.server; - }); - it.skip('only initialises api component if httpAdmin enabled'); - it.skip('stubs httpAdmin if httpAdmin disabled'); - it.skip('stubs httpNode if httpNode disabled'); - }); - -}); diff --git a/test/unit/node-red/red_spec.js b/test/unit/node-red/red_spec.js deleted file mode 100644 index ee46fc504..000000000 --- a/test/unit/node-red/red_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("node-red/red", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}); From 74955354cc542de62d4ec421f5518cc874f05428 Mon Sep 17 00:00:00 2001 From: "andrew.greene" Date: Wed, 8 Dec 2021 18:08:01 -0700 Subject: [PATCH 03/53] Add input/output to comment node --- .../@node-red/nodes/core/common/90-comment.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/common/90-comment.html b/packages/node_modules/@node-red/nodes/core/common/90-comment.html index cca7cd51d..579a92cc7 100644 --- a/packages/node_modules/@node-red/nodes/core/common/90-comment.html +++ b/packages/node_modules/@node-red/nodes/core/common/90-comment.html @@ -1,4 +1,4 @@ - + - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js deleted file mode 100644 index 73d364e43..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ /dev/null @@ -1,297 +0,0 @@ -module.exports = function(RED) { - "use strict"; - var util = require("util"); - var events = require("events"); - const fs = require("fs-extra"); - const path = require("path"); - var debuglength = RED.settings.debugMaxLength || 1000; - var useColors = RED.settings.debugUseColors || false; - util.inspect.styles.boolean = "red"; - - function DebugNode(n) { - var hasEditExpression = (n.targetType === "jsonata"); - var editExpression = hasEditExpression ? n.complete : null; - RED.nodes.createNode(this,n); - this.name = n.name; - this.complete = hasEditExpression ? null : (n.complete||"payload").toString(); - if (this.complete === "false") { this.complete = "payload"; } - this.console = ""+(n.console || false); - this.tostatus = n.tostatus || false; - this.statusType = n.statusType || "auto"; - this.statusVal = n.statusVal || this.complete; - this.tosidebar = n.tosidebar; - if (this.tosidebar === undefined) { this.tosidebar = true; } - this.active = (n.active === null || typeof n.active === "undefined") || n.active; - if (this.tostatus) { - this.status({fill:"grey", shape:"ring"}); - this.oldState = "{}"; - } - - var hasStatExpression = (n.statusType === "jsonata"); - var statExpression = hasStatExpression ? n.statusVal : null; - - var node = this; - var preparedEditExpression = null; - var preparedStatExpression = null; - if (editExpression) { - try { - preparedEditExpression = RED.util.prepareJSONataExpression(editExpression, this); - } - catch (e) { - node.error(RED._("debug.invalid-exp", {error: editExpression})); - return; - } - } - if (statExpression) { - try { - preparedStatExpression = RED.util.prepareJSONataExpression(statExpression, this); - } - catch (e) { - node.error(RED._("debug.invalid-exp", {error: editExpression})); - return; - } - } - - function prepareValue(msg, done) { - // Either apply the jsonata expression or... - if (preparedEditExpression) { - RED.util.evaluateJSONataExpression(preparedEditExpression, msg, (err, value) => { - if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); } - else { done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); } - }); - } else { - // Extract the required message property - var property = "payload"; - var output = msg[property]; - if (node.complete !== "false" && typeof node.complete !== "undefined") { - property = node.complete; - try { output = RED.util.getMessageProperty(msg,node.complete); } - catch(err) { output = undefined; } - } - done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, property:property, msg:output}); - } - } - - function prepareStatus(msg, done) { - if (node.statusType === "auto") { - if (node.complete === "true") { - done(null,{msg:msg.payload}); - } - else { - prepareValue(msg,function(err,debugMsg) { - if (err) { node.error(err); return; } - done(null,{msg:debugMsg.msg}); - }); - } - } - else { - // Either apply the jsonata expression or... - if (preparedStatExpression) { - RED.util.evaluateJSONataExpression(preparedStatExpression, msg, (err, value) => { - if (err) { done(RED._("debug.invalid-exp", {error:editExpression})); } - else { done(null,{msg:value}); } - }); - } - else { - // Extract the required message property - var output; - try { output = RED.util.getMessageProperty(msg,node.statusVal); } - catch(err) { output = undefined; } - done(null,{msg:output}); - } - } - } - this.on("close", function() { - if (this.oldState) { - this.status({}); - } - }) - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("status") && msg.status.hasOwnProperty("source") && msg.status.source.hasOwnProperty("id") && (msg.status.source.id === node.id)) { - done(); - return; - } - if (node.tostatus === true) { - prepareStatus(msg, function(err,debugMsg) { - if (err) { node.error(err); return; } - var output = debugMsg.msg; - var st = (typeof output === 'string') ? output : util.inspect(output); - var fill = "grey"; - var shape = "dot"; - if (typeof output === 'object' && output.hasOwnProperty("fill") && output.hasOwnProperty("shape") && output.hasOwnProperty("text")) { - fill = output.fill; - shape = output.shape; - st = output.text; - } - if (node.statusType === "auto") { - if (msg.hasOwnProperty("error")) { - fill = "red"; - st = msg.error.message; - } - if (msg.hasOwnProperty("status")) { - fill = msg.status.fill || "grey"; - shape = msg.status.shape || "ring"; - st = msg.status.text || ""; - } - } - - if (st.length > 32) { st = st.substr(0,32) + "..."; } - var newStatus = {fill:fill, shape:shape, text:st}; - if (JSON.stringify(newStatus) !== node.oldState) { // only send if we have to - node.status(newStatus); - node.oldState = JSON.stringify(newStatus); - } - }); - } - - if (this.complete === "true") { - // debug complete msg object - if (this.console === "true") { - node.log("\n"+util.inspect(msg, {colors:useColors, depth:10})); - } - if (this.active && this.tosidebar) { - sendDebug({id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:msg}); - } - done(); - } - else { - prepareValue(msg,function(err,debugMsg) { - if (err) { - node.error(err); - return; - } - var output = debugMsg.msg; - if (node.console === "true") { - if (typeof output === "string") { - node.log((output.indexOf("\n") !== -1 ? "\n" : "") + output); - } else if (typeof output === "object") { - node.log("\n"+util.inspect(output, {colors:useColors, depth:10})); - } else { - node.log(util.inspect(output, {colors:useColors})); - } - } - if (node.active) { - if (node.tosidebar == true) { - sendDebug(debugMsg); - } - } - done(); - }); - } - }) - } - - RED.nodes.registerType("debug",DebugNode, { - settings: { - debugUseColors: { - value: false, - }, - debugMaxLength: { - value: 1000, - } - } - }); - - function sendDebug(msg) { - // don't put blank errors in sidebar (but do add to logs) - //if ((msg.msg === "") && (msg.hasOwnProperty("level")) && (msg.level === 20)) { return; } - msg = RED.util.encodeObject(msg,{maxLength:debuglength}); - RED.comms.publish("debug",msg); - } - - DebugNode.logHandler = new events.EventEmitter(); - DebugNode.logHandler.on("log",function(msg) { - if (msg.level === RED.log.WARN || msg.level === RED.log.ERROR) { - sendDebug(msg); - } - }); - RED.log.addHandler(DebugNode.logHandler); - - function setNodeState(node,state) { - if (state) { - node.active = true; - } else { - node.active = false; - } - } - - RED.httpAdmin.post("/debug/:state", RED.auth.needsPermission("debug.write"), function(req,res) { - var state = req.params.state; - if (state !== 'enable' && state !== 'disable') { - res.sendStatus(404); - return; - } - var nodes = req.body && req.body.nodes; - if (Array.isArray(nodes)) { - nodes.forEach(function(id) { - var node = RED.nodes.getNode(id); - if (node !== null && typeof node !== "undefined" ) { - setNodeState(node, state === "enable"); - } - }) - res.sendStatus(state === "enable" ? 200 : 201); - } else { - res.sendStatus(400); - } - }) - - RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) { - var state = req.params.state; - if (state !== 'enable' && state !== 'disable') { - res.sendStatus(404); - return; - } - var node = RED.nodes.getNode(req.params.id); - if (node !== null && typeof node !== "undefined" ) { - setNodeState(node,state === "enable"); - res.sendStatus(state === "enable" ? 200 : 201); - } else { - res.sendStatus(404); - } - }); - - let cachedDebugView; - RED.httpAdmin.get("/debug/view/view.html", function(req,res) { - if (!cachedDebugView) { - fs.readFile(path.join(__dirname,"lib","debug","view.html")).then(data => { - let customStyles = ""; - try { - let customStyleList = RED.settings.editorTheme.page._.css || []; - customStyleList.forEach(style => { - customStyles += `\n` - }) - } catch(err) {} - cachedDebugView = data.toString().replace("",customStyles) - res.set('Content-Type', 'text/html'); - res.send(cachedDebugView).end(); - }).catch(err => { - res.sendStatus(404); - }) - } else { - res.send(cachedDebugView).end(); - } - - }); - - // As debug/view/debug-utils.js is loaded via - diff --git a/packages/node_modules/@node-red/nodes/core/common/24-complete.js b/packages/node_modules/@node-red/nodes/core/common/24-complete.js deleted file mode 100644 index ea665a265..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/24-complete.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function CompleteNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.scope = n.scope; - this.on("input",function(msg, send, done) { - send(msg); - done(); - }); - } - - RED.nodes.registerType("complete",CompleteNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/25-catch.html b/packages/node_modules/@node-red/nodes/core/common/25-catch.html deleted file mode 100644 index 0b976ea78..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-catch.html +++ /dev/null @@ -1,191 +0,0 @@ - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/25-catch.js b/packages/node_modules/@node-red/nodes/core/common/25-catch.js deleted file mode 100644 index 5ed525c36..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-catch.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function CatchNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.scope = n.scope; - this.uncaught = n.uncaught; - this.on("input",function(msg, send, done) { - send(msg); - done(); - }); - } - - RED.nodes.registerType("catch",CatchNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/25-status.html b/packages/node_modules/@node-red/nodes/core/common/25-status.html deleted file mode 100644 index 47a3192e4..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-status.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/25-status.js b/packages/node_modules/@node-red/nodes/core/common/25-status.js deleted file mode 100644 index fc6ccbe29..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/25-status.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function StatusNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.scope = n.scope; - this.on("input", function(msg, send, done) { - send(msg); - done(); - }); - } - - RED.nodes.registerType("status",StatusNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.html b/packages/node_modules/@node-red/nodes/core/common/60-link.html deleted file mode 100644 index f3fabba59..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.js b/packages/node_modules/@node-red/nodes/core/common/60-link.js deleted file mode 100644 index 53404e446..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - const crypto = require("crypto"); - - function LinkInNode(n) { - RED.nodes.createNode(this,n); - var node = this; - var event = "node:"+n.id; - var handler = function(msg) { - msg._event = n.event; - node.receive(msg); - } - RED.events.on(event,handler); - this.on("input", function(msg, send, done) { - send(msg); - done(); - }); - this.on("close",function() { - RED.events.removeListener(event,handler); - }); - } - - RED.nodes.registerType("link in",LinkInNode); - - function LinkOutNode(n) { - RED.nodes.createNode(this,n); - var node = this; - var mode = n.mode || "link"; - - var event = "node:"+n.id; - this.on("input", function(msg, send, done) { - msg._event = event; - RED.events.emit(event,msg) - - if (mode === "return") { - if (Array.isArray(msg._linkSource) && msg._linkSource.length > 0) { - var messageEvent = msg._linkSource.pop(); - var returnNode = RED.nodes.getNode(messageEvent.node); - if (returnNode && returnNode.returnLinkMessage) { - returnNode.returnLinkMessage(messageEvent.id, msg); - } else { - node.warn(RED._("link.error.missingReturn")) - } - } else { - node.warn(RED._("link.error.missingReturn")) - } - done(); - } else if (mode === "link") { - send(msg); - done(); - } - }); - } - RED.nodes.registerType("link out",LinkOutNode); - - - function LinkCallNode(n) { - RED.nodes.createNode(this,n); - const node = this; - const target = n.links[0]; - const messageEvents = {}; - let timeout = parseFloat(n.timeout || "30")*1000; - if (isNaN(timeout)) { - timeout = 30000; - } - - this.on("input", function(msg, send, done) { - msg._linkSource = msg._linkSource || []; - const messageEvent = { - id: crypto.randomBytes(14).toString('hex'), - node: node.id, - } - messageEvents[messageEvent.id] = { - msg: RED.util.cloneMessage(msg), - send, - done, - ts: setTimeout(function() { - timeoutMessage(messageEvent.id) - }, timeout ) - }; - msg._linkSource.push(messageEvent); - var targetNode = RED.nodes.getNode(target); - if (targetNode) { - targetNode.receive(msg); - } - }); - - this.returnLinkMessage = function(eventId, msg) { - if (Array.isArray(msg._linkSource) && msg._linkSource.length === 0) { - delete msg._linkSource; - } - const messageEvent = messageEvents[eventId]; - if (messageEvent) { - clearTimeout(messageEvent.ts); - delete messageEvents[eventId]; - messageEvent.send(msg); - messageEvent.done(); - } else { - node.send(msg); - } - } - - function timeoutMessage(eventId) { - const messageEvent = messageEvents[eventId]; - if (messageEvent) { - delete messageEvents[eventId]; - node.error("timeout",messageEvent.msg); - } - } - - } - RED.nodes.registerType("link call",LinkCallNode); - - -} diff --git a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html b/packages/node_modules/@node-red/nodes/core/common/98-unknown.html deleted file mode 100644 index 52071c30f..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/98-unknown.js b/packages/node_modules/@node-red/nodes/core/common/98-unknown.js deleted file mode 100644 index 0ee463bb3..000000000 --- a/packages/node_modules/@node-red/nodes/core/common/98-unknown.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - function UnknownNode(n) { - RED.nodes.createNode(this,n); - } - RED.nodes.registerType("unknown",UnknownNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.html b/packages/node_modules/@node-red/nodes/core/function/10-switch.html deleted file mode 100644 index f6fc82f0a..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.html +++ /dev/null @@ -1,440 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.js b/packages/node_modules/@node-red/nodes/core/function/10-switch.js deleted file mode 100644 index aa1972221..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.js +++ /dev/null @@ -1,521 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var operators = { - 'eq': function(a, b) { return a == b; }, - 'neq': function(a, b) { return a != b; }, - 'lt': function(a, b) { return a < b; }, - 'lte': function(a, b) { return a <= b; }, - 'gt': function(a, b) { return a > b; }, - 'gte': function(a, b) { return a >= b; }, - 'btwn': function(a, b, c) { return (a >= b && a <= c) || (a <= b && a >= c); }, - 'cont': function(a, b) { return (a + "").indexOf(b) != -1; }, - 'regex': function(a, b, c, d) { return (a + "").match(new RegExp(b,d?'i':'')); }, - 'true': function(a) { return a === true; }, - 'false': function(a) { return a === false; }, - 'null': function(a) { return (typeof a == "undefined" || a === null); }, - 'nnull': function(a) { return (typeof a != "undefined" && a !== null); }, - 'empty': function(a) { - if (typeof a === 'string' || Array.isArray(a) || Buffer.isBuffer(a)) { - return a.length === 0; - } else if (typeof a === 'object' && a !== null) { - return Object.keys(a).length === 0; - } - return false; - }, - 'nempty': function(a) { - if (typeof a === 'string' || Array.isArray(a) || Buffer.isBuffer(a)) { - return a.length !== 0; - } else if (typeof a === 'object' && a !== null) { - return Object.keys(a).length !== 0; - } - return false; - }, - 'istype': function(a, b) { - if (b === "array") { return Array.isArray(a); } - else if (b === "buffer") { return Buffer.isBuffer(a); } - else if (b === "json") { - try { JSON.parse(a); return true; } // or maybe ??? a !== null; } - catch(e) { return false;} - } - else if (b === "null") { return a === null; } - else { return typeof a === b && !Array.isArray(a) && !Buffer.isBuffer(a) && a !== null; } - }, - 'head': function(a, b, c, d, parts) { - var count = Number(b); - return (parts.index < count); - }, - 'tail': function(a, b, c, d, parts) { - var count = Number(b); - return (parts.count -count <= parts.index); - }, - 'index': function(a, b, c, d, parts) { - var min = Number(b); - var max = Number(c); - var index = parts.index; - return ((min <= index) && (index <= max)); - }, - 'hask': function(a, b) { - return a !== undefined && a !== null && (typeof b !== "object" ) && a.hasOwnProperty(b+""); - }, - 'jsonata_exp': function(a, b) { return (b === true); }, - 'else': function(a) { return a === true; } - }; - - var _maxKeptCount; - - function getMaxKeptCount() { - if (_maxKeptCount === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _maxKeptCount = RED.settings[name]; - } - else { - _maxKeptCount = 0; - } - } - return _maxKeptCount; - } - - function getProperty(node,msg,done) { - if (node.propertyType === 'jsonata') { - RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => { - if (err) { - done(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - done(undefined,value); - } - }); - } else { - RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => { - if (err) { - done(undefined,undefined); - } else { - done(undefined,value); - } - }); - } - } - - function getV1(node,msg,rule,hasParts,done) { - if (rule.vt === 'prev') { - return done(undefined,node.previousValue); - } else if (rule.vt === 'jsonata') { - var exp = rule.v; - if (rule.t === 'jsonata_exp') { - if (hasParts) { - exp.assign("I", msg.parts.index); - exp.assign("N", msg.parts.count); - } - } - RED.util.evaluateJSONataExpression(exp,msg,(err,value) => { - if (err) { - done(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - done(undefined, value); - } - }); - } else if (rule.vt === 'json') { - done(undefined,"json"); // TODO: ?! invalid case - } else if (rule.vt === 'null') { - done(undefined,"null"); - } else { - RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) { - if (err) { - done(undefined, undefined); - } else { - done(undefined, value); - } - }); - } - } - - function getV2(node,msg,rule,done) { - var v2 = rule.v2; - if (rule.v2t === 'prev') { - return done(undefined,node.previousValue); - } else if (rule.v2t === 'jsonata') { - RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => { - if (err) { - done(RED._("switch.errors.invalid-expr",{error:err.message})); - } else { - done(undefined,value); - } - }); - } else if (typeof v2 !== 'undefined') { - RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) { - if (err) { - done(undefined,undefined); - } else { - done(undefined,value); - } - }); - } else { - done(undefined,v2); - } - } - - function applyRule(node, msg, property, state, done) { - var rule = node.rules[state.currentRule]; - var v1,v2; - - getV1(node,msg,rule,state.hasParts, (err,value) => { - if (err) { - // This only happens if v1 is an invalid JSONata expr - // But that will have already been logged and the node marked - // invalid as part of the constructor - return done(err); - } - v1 = value; - getV2(node,msg,rule, (err,value) => { - if (err) { - // This only happens if v1 is an invalid JSONata expr - // But that will have already been logged and the node marked - // invalid as part of the constructor - return done(err); - } - v2 = value; - if (rule.t == "else") { - property = state.elseflag; - state.elseflag = true; - } - try { - if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) { - state.onward.push(msg); - state.elseflag = false; - if (node.checkall == "false") { - return done(undefined,false); - } - } else { - state.onward.push(null); - } - done(undefined, state.currentRule < node.rules.length - 1); - } catch(err) { - // An error occurred evaluating the rule - for example, an - // invalid RegExp value. - done(err); - } - }); - }); - } - - function applyRules(node, msg, property,state,done) { - if (!state) { - if (node.rules.length === 0) { - done(undefined, []); - return; - } - state = { - currentRule: 0, - elseflag: true, - onward: [], - hasParts: msg.hasOwnProperty("parts") && - msg.parts.hasOwnProperty("id") && - msg.parts.hasOwnProperty("index") - } - } - applyRule(node,msg,property,state,(err,hasMore) => { - if (err) { - return done(err); - } - if (hasMore) { - state.currentRule++; - applyRules(node,msg,property,state,done); - } else { - node.previousValue = property; - done(undefined,state.onward); - } - }); - } - - - function SwitchNode(n) { - RED.nodes.createNode(this, n); - this.rules = n.rules || []; - this.property = n.property; - this.propertyType = n.propertyType || "msg"; - - if (this.propertyType === 'jsonata') { - try { - this.property = RED.util.prepareJSONataExpression(this.property,this); - } catch(err) { - this.error(RED._("switch.errors.invalid-expr",{error:err.message})); - return; - } - } - - this.checkall = n.checkall || "true"; - this.previousValue = null; - var node = this; - var valid = true; - var repair = n.repair; - var needsCount = repair; - - for (var i=0; i 0) && (pendingCount > max_msgs)) { - clearPending(); - node.error(RED._("switch.errors.too-many"), msg); - } - if (parts.hasOwnProperty("count")) { - group.count = parts.count; - } - return group; - } - - function drainMessageGroup(msgs,count,done) { - var msg = msgs.shift(); - msg.parts.count = count; - processMessage(msg,false, err => { - if (err) { - done(err); - } else { - if (msgs.length === 0) { - done() - } else { - drainMessageGroup(msgs,count,done); - } - } - }) - } - function addMessageToPending(msg,done) { - var parts = msg.parts; - // We've already checked the msg.parts has the require bits - var group = addMessageToGroup(parts.id, msg, parts); - var msgs = group.msgs; - var count = group.count; - var msgsCount = msgs.length; - if (count === msgsCount) { - // We have a complete group - send the individual parts - drainMessageGroup(msgs,count,err => { - pendingCount -= msgsCount; - delete pendingIn[parts.id]; - done(); - }) - return; - } - done(); - } - - function sendGroup(onwards, port_count) { - var counts = new Array(port_count).fill(0); - for (var i = 0; i < onwards.length; i++) { - var onward = onwards[i]; - for (var j = 0; j < port_count; j++) { - counts[j] += (onward[j] !== null) ? 1 : 0 - } - } - var ids = new Array(port_count); - for (var j = 0; j < port_count; j++) { - ids[j] = RED.util.generateId(); - } - var ports = new Array(port_count); - var indexes = new Array(port_count).fill(0); - for (var i = 0; i < onwards.length; i++) { - var onward = onwards[i]; - for (var j = 0; j < port_count; j++) { - var msg = onward[j]; - if (msg) { - var new_msg = RED.util.cloneMessage(msg); - var parts = new_msg.parts; - parts.id = ids[j]; - parts.index = indexes[j]; - parts.count = counts[j]; - ports[j] = new_msg; - indexes[j]++; - } - else { - ports[j] = null; - } - } - node.send(ports); - } - } - - function sendGroupMessages(onward, msg) { - var parts = msg.parts; - var gid = parts.id; - received[gid] = ((gid in received) ? received[gid] : 0) +1; - var send_ok = (received[gid] === parts.count); - - if (!(gid in pendingOut)) { - pendingOut[gid] = { - onwards: [] - }; - } - var group = pendingOut[gid]; - var onwards = group.onwards; - onwards.push(onward); - pendingCount++; - if (send_ok) { - sendGroup(onwards, onward.length, msg); - pendingCount -= onward.length; - delete pendingOut[gid]; - delete received[gid]; - } - var max_msgs = getMaxKeptCount(); - if ((max_msgs > 0) && (pendingCount > max_msgs)) { - clearPending(); - node.error(RED._("switch.errors.too-many"), msg); - } - } - - function processMessage(msg, checkParts, done) { - var hasParts = msg.hasOwnProperty("parts") && - msg.parts.hasOwnProperty("id") && - msg.parts.hasOwnProperty("index"); - - if (needsCount && checkParts && hasParts) { - addMessageToPending(msg,done); - } else { - getProperty(node,msg,(err,property) => { - if (err) { - node.warn(err); - done(); - } else { - applyRules(node,msg,property,undefined,(err,onward) => { - if (err) { - node.error(err, msg); - } else { - if (!repair || !hasParts) { - node.send(onward); - } else { - sendGroupMessages(onward, msg); - } - } - done(); - }); - } - }); - } - } - - function clearPending() { - pendingCount = 0; - pendingId = 0; - pendingIn = {}; - pendingOut = {}; - received = {}; - } - - var pendingMessages = []; - var handlingMessage = false; - var processMessageQueue = function(msg) { - if (msg) { - - // A new message has arrived - add it to the message queue - pendingMessages.push(msg); - if (handlingMessage) { - // The node is currently processing a message, so do nothing - // more with this message - return; - } - } - if (pendingMessages.length === 0) { - // There are no more messages to process, clear the active flag - // and return - handlingMessage = false; - return; - } - - // There are more messages to process. Get the next message and - // start processing it. Recurse back in to check for any more - var nextMsg = pendingMessages.shift(); - handlingMessage = true; - processMessage(nextMsg,true,err => { - if (err) { - node.error(err,nextMsg); - } - processMessageQueue() - }); - } - - this.on('input', function(msg) { - processMessageQueue(msg); - }); - - this.on('close', function() { - clearPending(); - }); - } - - RED.nodes.registerType("switch", SwitchNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.html b/packages/node_modules/@node-red/nodes/core/function/15-change.html deleted file mode 100644 index b40039028..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/15-change.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.js b/packages/node_modules/@node-red/nodes/core/function/15-change.js deleted file mode 100644 index d177caec8..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/15-change.js +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function ChangeNode(n) { - RED.nodes.createNode(this, n); - var node = this; - - this.rules = n.rules; - var rule; - if (!this.rules) { - rule = { - t:(n.action=="replace"?"set":n.action), - p:n.property||"" - } - - if ((rule.t === "set")||(rule.t === "move")) { - rule.to = n.to||""; - } else if (rule.t === "change") { - rule.from = n.from||""; - rule.to = n.to||""; - rule.re = (n.reg===null||n.reg); - } - this.rules = [rule]; - } - - var valid = true; - for (var i=0;i { - if (err) { - done(undefined,undefined); - } else { - done(undefined,value); - } - }); - return - } else if (rule.tot === 'date') { - value = Date.now(); - } else if (rule.tot === 'jsonata') { - RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => { - if (err) { - done(RED._("change.errors.invalid-expr",{error:err.message})) - } else { - done(undefined, value); - } - }); - return; - } - done(undefined,value); - } - - function getFromValueType(fromValue, done) { - var fromType; - var fromRE; - if (typeof fromValue === 'number' || fromValue instanceof Number) { - fromType = 'num'; - } else if (typeof fromValue === 'boolean') { - fromType = 'bool' - } else if (fromValue instanceof RegExp) { - fromType = 're'; - fromRE = fromValue; - } else if (typeof fromValue === 'string') { - fromType = 'str'; - fromRE = fromValue.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - try { - fromRE = new RegExp(fromRE, "g"); - } catch (e) { - done(new Error(RED._("change.errors.invalid-from",{error:e.message}))); - } - } else { - done(new Error(RED._("change.errors.invalid-from",{error:"unsupported type: "+(typeof fromValue)}))); - } - done(undefined,{ - fromType, - fromValue, - fromRE - }); - } - function getFromValue(msg,rule, done) { - var fromValue; - var fromType; - var fromRE; - if (rule.t === 'change') { - if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') { - if (rule.fromt === "msg") { - return getFromValueType(RED.util.getMessageProperty(msg,rule.from),done); - } else if (rule.fromt === 'flow' || rule.fromt === 'global') { - var contextKey = RED.util.parseContextStore(rule.from); - if (/\[msg\./.test(context.key)) { - // The key has a nest msg. reference to evaluate first - context.key = RED.util.normalisePropertyExpression(contextKey.key,msg,true); - } - node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => { - if (err) { - done(err) - } else { - getFromValueType(fromValue,done); - } - }); - return; - } - } else { - fromType = rule.fromt; - fromValue = rule.from; - fromRE = rule.fromRE; - } - } - done(undefined, { - fromType, - fromValue, - fromRE - }); - } - function applyRule(msg,rule,done) { - var property = rule.p; - var current; - var fromValue; - var fromType; - var fromRE; - - try { - getToValue(msg,rule,(err,value) => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } else { - if (rule.dc) { - value = RED.util.cloneMessage(value); - } - getFromValue(msg,rule,(err,fromParts) => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } else { - fromValue = fromParts.fromValue; - fromType = fromParts.fromType; - fromRE = fromParts.fromRE; - if (rule.pt === 'msg') { - try { - if (rule.t === 'delete') { - RED.util.setMessageProperty(msg,property,undefined); - } else if (rule.t === 'set') { - if (!RED.util.setMessageProperty(msg,property,value)) { - node.warn(RED._("change.errors.no-override",{property:property})); - } - } else if (rule.t === 'change') { - current = RED.util.getMessageProperty(msg,property); - if (typeof current === 'string') { - if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) { - // str representation of exact from number/boolean - // only replace if they match exactly - RED.util.setMessageProperty(msg,property,value); - } else { - current = current.replace(fromRE,value); - RED.util.setMessageProperty(msg,property,current); - } - } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') { - if (current == Number(fromValue)) { - RED.util.setMessageProperty(msg,property,value); - } - } else if (typeof current === 'boolean' && fromType === 'bool') { - if (current.toString() === fromValue) { - RED.util.setMessageProperty(msg,property,value); - } - } - } - } catch(err) {} - return done(undefined,msg); - } else if (rule.pt === 'flow' || rule.pt === 'global') { - var contextKey = RED.util.parseContextStore(property); - if (/\[msg/.test(contextKey.key)) { - // The key has a nest msg. reference to evaluate first - contextKey.key = RED.util.normalisePropertyExpression(contextKey.key, msg, true) - } - var target = node.context()[rule.pt]; - var callback = err => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } else { - done(undefined,msg); - } - } - if (rule.t === 'delete') { - target.set(contextKey.key,undefined,contextKey.store,callback); - } else if (rule.t === 'set') { - target.set(contextKey.key,value,contextKey.store,callback); - } else if (rule.t === 'change') { - target.get(contextKey.key,contextKey.store,(err,current) => { - if (err) { - node.error(err, msg); - return done(undefined,null); - } - if (typeof current === 'string') { - if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) { - // str representation of exact from number/boolean - // only replace if they match exactly - target.set(contextKey.key,value,contextKey.store,callback); - } else { - current = current.replace(fromRE,value); - target.set(contextKey.key,current,contextKey.store,callback); - } - } else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') { - if (current == Number(fromValue)) { - target.set(contextKey.key,value,contextKey.store,callback); - } - } else if (typeof current === 'boolean' && fromType === 'bool') { - if (current.toString() === fromValue) { - target.set(contextKey.key,value,contextKey.store,callback); - } - } - }); - } - } - } - }) - } - }); - } catch(err) { - // This is an okay error - done(undefined,msg); - } - } - function completeApplyingRules(msg,currentRule,done) { - if (!msg) { - return done(); - } else if (currentRule === node.rules.length - 1) { - return done(undefined, msg); - } else { - applyRules(msg, currentRule+1,done); - } - } - function applyRules(msg, currentRule, done) { - if (currentRule >= node.rules.length) { - return done(undefined,msg); - } - var r = node.rules[currentRule]; - if (r.t === "move") { - if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) { - applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => { - applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => { - completeApplyingRules(msg,currentRule,done); - }) - }); - } else { // 2 step move if we are moving from a child - applyRule(msg,{t:"set", p:"_temp_move", pt:r.tot, to:r.p, tot:r.pt},(err,msg)=> { - applyRule(msg,{t:"delete", p:r.p, pt:r.pt},(err,msg)=> { - applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:"_temp_move", tot:r.pt},(err,msg)=> { - applyRule(msg,{t:"delete", p:"_temp_move", pt:r.pt},(err,msg)=> { - completeApplyingRules(msg,currentRule,done); - }); - }); - }); - }); - } - } else { - applyRule(msg,r,(err,msg)=> { completeApplyingRules(msg,currentRule,done); }); - } - } - - if (valid) { - this.on('input', function(msg, send, done) { - applyRules(msg, 0, (err,msg) => { - if (err) { - done(err); - } else if (msg) { - send(msg); - done(); - } - }) - }); - } - } - RED.nodes.registerType("change", ChangeNode); -}; diff --git a/packages/node_modules/@node-red/nodes/core/function/16-range.html b/packages/node_modules/@node-red/nodes/core/function/16-range.html deleted file mode 100644 index f108a99ce..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/16-range.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/16-range.js b/packages/node_modules/@node-red/nodes/core/function/16-range.js deleted file mode 100644 index a5dede4ea..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/16-range.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - function RangeNode(n) { - RED.nodes.createNode(this, n); - this.action = n.action; - this.round = n.round || false; - this.minin = Number(n.minin); - this.maxin = Number(n.maxin); - this.minout = Number(n.minout); - this.maxout = Number(n.maxout); - this.property = n.property||"payload"; - var node = this; - - this.on('input', function (msg, send, done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var n = Number(value); - if (!isNaN(n)) { - if (node.action == "clamp") { - if (n < node.minin) { n = node.minin; } - if (n > node.maxin) { n = node.maxin; } - } - if (node.action == "roll") { - var divisor = node.maxin - node.minin; - n = ((n - node.minin) % divisor + divisor) % divisor + node.minin; - } - value = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout; - if (node.round) { value = Math.round(value); } - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - } - else { node.log(RED._("range.errors.notnumber")+": "+value); } - } - else { send(msg); } // If no payload - just pass it on. - done(); - }); - } - RED.nodes.registerType("range", RangeNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/80-template.html b/packages/node_modules/@node-red/nodes/core/function/80-template.html deleted file mode 100644 index 98d4a0d37..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/80-template.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/80-template.js b/packages/node_modules/@node-red/nodes/core/function/80-template.js deleted file mode 100644 index d4c27cfd0..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/80-template.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var yaml = require("js-yaml"); - - function extractTokens(tokens,set) { - set = set || new Set(); - tokens.forEach(function(token) { - if (token[0] !== 'text') { - set.add(token[1]); - if (token.length > 4) { - extractTokens(token[4],set); - } - } - }); - return set; - } - - function parseContext(key) { - var match = /^(flow|global)(\[(\w+)\])?\.(.+)/.exec(key); - if (match) { - var parts = {}; - parts.type = match[1]; - parts.store = (match[3] === '') ? "default" : match[3]; - parts.field = match[4]; - return parts; - } - return undefined; - } - - /** - * Custom Mustache Context capable to collect message property and node - * flow and global context - */ - - function NodeContext(msg, nodeContext, parent, escapeStrings, cachedContextTokens) { - this.msgContext = new mustache.Context(msg,parent); - this.nodeContext = nodeContext; - this.escapeStrings = escapeStrings; - this.cachedContextTokens = cachedContextTokens; - } - - NodeContext.prototype = new mustache.Context(); - - NodeContext.prototype.lookup = function (name) { - // try message first: - try { - var value = this.msgContext.lookup(name); - if (value !== undefined) { - if (this.escapeStrings && typeof value === "string") { - value = value.replace(/\\/g, "\\\\"); - value = value.replace(/\n/g, "\\n"); - value = value.replace(/\t/g, "\\t"); - value = value.replace(/\r/g, "\\r"); - value = value.replace(/\f/g, "\\f"); - value = value.replace(/[\b]/g, "\\b"); - } - return value; - } - - // try flow/global context: - var context = parseContext(name); - if (context) { - var type = context.type; - var store = context.store; - var field = context.field; - var target = this.nodeContext[type]; - if (target) { - return this.cachedContextTokens[name]; - } - } - return ''; - } - catch(err) { - throw err; - } - } - - NodeContext.prototype.push = function push (view) { - return new NodeContext(view, this.nodeContext, this.msgContext, undefined, this.cachedContextTokens); - }; - - function TemplateNode(n) { - RED.nodes.createNode(this,n); - this.name = n.name; - this.field = n.field || "payload"; - this.template = n.template; - this.syntax = n.syntax || "mustache"; - this.fieldType = n.fieldType || "msg"; - this.outputFormat = n.output || "str"; - - var node = this; - - function output(msg,value,send,done) { - /* istanbul ignore else */ - if (node.outputFormat === "json") { - value = JSON.parse(value); - } - /* istanbul ignore else */ - if (node.outputFormat === "yaml") { - value = yaml.load(value); - } - - if (node.fieldType === 'msg') { - RED.util.setMessageProperty(msg, node.field, value); - send(msg); - done(); - } else if ((node.fieldType === 'flow') || - (node.fieldType === 'global')) { - var context = RED.util.parseContextStore(node.field); - var target = node.context()[node.fieldType]; - target.set(context.key, value, context.store, function (err) { - if (err) { - done(err); - } else { - send(msg); - done(); - } - }); - } - } - - node.on("input", function(msg, send, done) { - - try { - /*** - * Allow template contents to be defined externally - * through inbound msg.template IFF node.template empty - */ - var template = node.template; - if (msg.hasOwnProperty("template")) { - if (template == "" || template === null) { - template = msg.template; - } - } - - if (node.syntax === "mustache") { - var is_json = (node.outputFormat === "json"); - var promises = []; - var tokens = extractTokens(mustache.parse(template)); - var resolvedTokens = {}; - tokens.forEach(function(name) { - var context = parseContext(name); - if (context) { - var type = context.type; - var store = context.store; - var field = context.field; - var target = node.context()[type]; - if (target) { - var promise = new Promise((resolve, reject) => { - target.get(field, store, (err, val) => { - if (err) { - reject(err); - } else { - resolvedTokens[name] = val; - resolve(); - } - }); - }); - promises.push(promise); - return; - } - } - }); - - Promise.all(promises).then(function() { - var value = mustache.render(template, new NodeContext(msg, node.context(), null, is_json, resolvedTokens)); - output(msg, value, send, done); - }).catch(function (err) { - done(err.message); - }); - } else { - output(msg, template, send, done); - } - } - catch(err) { - done(err.message); - } - }); - } - - RED.nodes.registerType("template",TemplateNode); - RED.library.register("templates"); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.js b/packages/node_modules/@node-red/nodes/core/function/89-delay.js deleted file mode 100644 index 5205a5b18..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.js +++ /dev/null @@ -1,424 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -//Simple node to introduce a pause into a flow -module.exports = function(RED) { - "use strict"; - - var MILLIS_TO_NANOS = 1000000; - var SECONDS_TO_NANOS = 1000000000; - var _maxKeptMsgsCount; - - function maxKeptMsgsCount(node) { - if (_maxKeptMsgsCount === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _maxKeptMsgsCount = RED.settings[name]; - } - else { - _maxKeptMsgsCount = 0; - } - } - return _maxKeptMsgsCount; - } - - function DelayNode(n) { - RED.nodes.createNode(this,n); - - this.pauseType = n.pauseType; - this.timeoutUnits = n.timeoutUnits; - this.randomUnits = n.randomUnits; - this.rateUnits = n.rateUnits; - - if (n.timeoutUnits === "milliseconds") { - this.timeout = n.timeout; - } else if (n.timeoutUnits === "minutes") { - this.timeout = n.timeout * (60 * 1000); - } else if (n.timeoutUnits === "hours") { - this.timeout = n.timeout * (60 * 60 * 1000); - } else if (n.timeoutUnits === "days") { - this.timeout = n.timeout * (24 * 60 * 60 * 1000); - } else { // Default to seconds - this.timeout = n.timeout * 1000; - } - - if (n.rateUnits === "minute") { - this.rate = (60 * 1000)/n.rate; - } else if (n.rateUnits === "hour") { - this.rate = (60 * 60 * 1000)/n.rate; - } else if (n.rateUnits === "day") { - this.rate = (24 * 60 * 60 * 1000)/n.rate; - } else { // Default to seconds - this.rate = 1000/n.rate; - } - - this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1); - - if (n.randomUnits === "milliseconds") { - this.randomFirst = n.randomFirst * 1; - this.randomLast = n.randomLast * 1; - } else if (n.randomUnits === "minutes") { - this.randomFirst = n.randomFirst * (60 * 1000); - this.randomLast = n.randomLast * (60 * 1000); - } else if (n.randomUnits === "hours") { - this.randomFirst = n.randomFirst * (60 * 60 * 1000); - this.randomLast = n.randomLast * (60 * 60 * 1000); - } else if (n.randomUnits === "days") { - this.randomFirst = n.randomFirst * (24 * 60 * 60 * 1000); - this.randomLast = n.randomLast * (24 * 60 * 60 * 1000); - } else { // Default to seconds - this.randomFirst = n.randomFirst * 1000; - this.randomLast = n.randomLast * 1000; - } - - this.diff = this.randomLast - this.randomFirst; - this.name = n.name; - this.idList = []; - this.buffer = []; - this.intervalID = -1; - this.randomID = -1; - this.lastSent = null; - this.drop = n.drop; - this.droppedMsgs = 0; - this.allowrate = n.allowrate|| false; - this.fixedrate = this.rate; - this.outputs = n.outputs; - var node = this; - - function ourTimeout(handler, delay, clearHandler) { - var toutID = setTimeout(handler, delay); - return { - clear: function() { clearTimeout(toutID); clearHandler(); }, - trigger: function() { clearTimeout(toutID); return handler(); } - }; - } - - var sendMsgFromBuffer = function() { - if (node.buffer.length === 0) { - clearInterval(node.intervalID); - node.intervalID = -1; - } - if (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - if (Object.keys(msgInfo.msg).length > 1) { - msgInfo.send(msgInfo.msg); - msgInfo.done(); - } - } - node.reportDepth(); - } - - var clearDelayList = function(s) { - var len = node.idList.length; - for (var i=0; i 0) { node.status({text:node.buffer.length}); } - // else { node.status({}); } - node.status({fill:"blue",shape:"dot",text:node.buffer.length}); - node.busy = null; - }, 500); - } - } - - var loggerId = setInterval(function () { - if (node.droppedMsgs !== 0) { - node.debug("node.droppedMsgs = " + node.droppedMsgs); - node.droppedMsgs = 0; - } - }, 15 * 1000); - node.on("close", function() { clearInterval(loggerId); }); - - // The delay type modes - if (node.pauseType === "delay") { - node.on("input", function(msg, send, done) { - var id = ourTimeout(function() { - node.idList.splice(node.idList.indexOf(id),1); - if (node.timeout > 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - send(msg); - done(); - }, node.timeout, () => done()); - if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); } - else { node.idList.push(id); } - if (msg.hasOwnProperty("reset")) { clearDelayList(true); } - else if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); } - else if (node.timeout > 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - }); - node.on("close", function() { clearDelayList(); }); - } - else if (node.pauseType === "delayv") { - node.on("input", function(msg, send, done) { - var delayvar = Number(node.timeout); - if (msg.hasOwnProperty("delay") && !isNaN(parseFloat(msg.delay))) { - delayvar = parseFloat(msg.delay); - } - if (delayvar < 0) { delayvar = 0; } - var id = ourTimeout(function() { - node.idList.splice(node.idList.indexOf(id),1); - if (node.idList.length === 0) { node.status({}); } - send(msg); - if (delayvar >= 0) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - done(); - }, delayvar, () => done()); - node.idList.push(id); - if (msg.hasOwnProperty("reset")) { clearDelayList(true); } - if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); } - if (delayvar >= 0) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - }); - node.on("close", function() { clearDelayList(); }); - } - else if (node.pauseType === "random") { - node.on("input", function(msg, send, done) { - var wait = node.randomFirst + (node.diff * Math.random()); - var id = ourTimeout(function() { - node.idList.splice(node.idList.indexOf(id),1); - send(msg); - if (node.timeout >= 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - done(); - }, wait, () => done()); - if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); } - else { node.idList.push(id); } - if (msg.hasOwnProperty("reset")) { clearDelayList(true); } - if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); } - if (node.timeout >= 1000) { - node.status({fill:"blue",shape:"dot",text:node.idList.length}); - } - }); - node.on("close", function() { clearDelayList(); }); - } - - // The rate limit/queue type modes - else if (node.pauseType === "rate") { - node.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - if (node.intervalID !== -1 ) { - clearInterval(node.intervalID); - node.intervalID = -1; - } - delete node.lastSent; - node.buffer = []; - node.rate = node.fixedrate; - node.status({fill:"blue",shape:"ring",text:0}); - done(); - return; - } - - if (!node.drop) { - var m = RED.util.cloneMessage(msg); - delete m.flush; - delete m.lifo; - if (Object.keys(m).length > 1) { - if (node.intervalID !== -1) { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - var max_msgs = maxKeptMsgsCount(node); - if ((max_msgs > 0) && (node.buffer.length >= max_msgs)) { - node.buffer = []; - node.error(RED._("delay.errors.too-many"), msg); - } else if (msg.toFront === true) { - node.buffer.unshift({msg: m, send: send, done: done}); - node.reportDepth(); - } else { - node.buffer.push({msg: m, send: send, done: done}); - node.reportDepth(); - } - } - else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { - node.rate = msg.rate; - } - send(m); - node.reportDepth(); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - done(); - } - } - if (msg.hasOwnProperty("flush")) { - var len = node.buffer.length; - if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush),len); } - while (len > 0) { - const msgInfo = node.buffer.shift(); - if (Object.keys(msgInfo.msg).length > 1) { - node.send(msgInfo.msg); - msgInfo.done(); - } - len = len - 1; - } - if (node.buffer.length === 0) { - clearInterval(node.intervalID); - node.intervalID = -1; - } - node.status({fill:"blue",shape:"dot",text:node.buffer.length}); - done(); - } - } - else { - if (maxKeptMsgsCount(node) > 0) { - if (node.intervalID === -1) { - node.send(msg); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - if (node.buffer.length < _maxKeptMsgsCount) { - var m = RED.util.cloneMessage(msg); - node.buffer.push({msg: m, send: send, done: done}); - } else { - node.trace("dropped due to buffer overflow. msg._msgid = " + msg._msgid); - node.droppedMsgs++; - } - } - } else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { - node.rate = msg.rate; - } - var timeSinceLast; - if (node.lastSent) { - timeSinceLast = process.hrtime(node.lastSent); - } - if (!node.lastSent) { // ensuring that we always send the first message - node.lastSent = process.hrtime(); - send(msg); - } - else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) { - node.lastSent = process.hrtime(); - send(msg); - } else if (node.outputs === 2) { - send([null,msg]) - } - } - done(); - } - }); - node.on("close", function() { - clearInterval(node.intervalID); - clearTimeout(node.busy); - node.buffer.forEach((msgInfo) => msgInfo.done()); - node.buffer = []; - node.status({}); - }); - } - - // The topic based fair queue and last arrived on all topics queue - else if ((node.pauseType === "queue") || (node.pauseType === "timed")) { - node.intervalID = setInterval(function() { - if (node.pauseType === "queue") { - if (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - msgInfo.send(msgInfo.msg); // send the first on the queue - msgInfo.done(); - } - } - else { - while (node.buffer.length > 0) { // send the whole queue - const msgInfo = node.buffer.shift(); - msgInfo.send(msgInfo.msg); - msgInfo.done(); - } - } - node.reportDepth(); - },node.rate); - - var hit; - node.on("input", function(msg, send, done) { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - if (!msg.hasOwnProperty("topic")) { msg.topic = "_none_"; } - hit = false; - for (var b in node.buffer) { // check if already in queue - if (msg.topic === node.buffer[b].msg.topic) { - if (node.outputs === 2) { send([null,node.buffer[b].msg]) } - node.buffer[b].done(); - node.buffer[b] = {msg, send, done}; // if so - replace existing entry - hit = true; - break; - } - } - if (!hit) { - node.buffer.push({msg, send, done}); // if not add to end of queue - node.reportDepth(); - } - if (msg.hasOwnProperty("reset")) { - while (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - msgInfo.done(); - } - node.buffer = []; - node.rate = node.fixedrate; - node.status({text:"reset"}); - done(); - } - if (msg.hasOwnProperty("flush")) { - var len = node.buffer.length; - if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush,len)); } - while (len > 0) { - const msgInfo = node.buffer.shift(); - delete msgInfo.msg.flush; - if (Object.keys(msgInfo.msg).length > 2) { - node.send(msgInfo.msg); - msgInfo.done(); - } - len = len - 1; - } - node.status({}); - done(); - } - }); - node.on("close", function() { - clearInterval(node.intervalID); - while (node.buffer.length > 0) { - const msgInfo = node.buffer.shift(); - msgInfo.done(); - } - node.buffer = []; - node.status({}); - }); - } - } - RED.nodes.registerType("delay",DelayNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html b/packages/node_modules/@node-red/nodes/core/function/89-trigger.html deleted file mode 100644 index a7ab0356f..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html +++ /dev/null @@ -1,234 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js deleted file mode 100644 index 16a00e99d..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js +++ /dev/null @@ -1,298 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - function TriggerNode(n) { - RED.nodes.createNode(this,n); - this.bytopic = n.bytopic || "all"; - this.op1 = n.op1 || "1"; - this.op2 = n.op2 || "0"; - this.op1type = n.op1type || "str"; - this.op2type = n.op2type || "str"; - this.second = (n.outputs == 2) ? true : false; - this.topic = n.topic || "topic"; - - if (this.op1type === 'val') { - if (this.op1 === 'true' || this.op1 === 'false') { - this.op1type = 'bool' - } else if (this.op1 === 'null') { - this.op1type = 'null'; - this.op1 = null; - } else { - this.op1type = 'str'; - } - } - if (this.op2type === 'val') { - if (this.op2 === 'true' || this.op2 === 'false') { - this.op2type = 'bool' - } else if (this.op2 === 'null') { - this.op2type = 'null'; - this.op2 = null; - } else { - this.op2type = 'str'; - } - } - this.extend = n.extend || "false"; - this.overrideDelay = n.overrideDelay || false; - this.units = n.units || "ms"; - this.reset = n.reset || ''; - this.duration = parseFloat(n.duration); - if (isNaN(this.duration)) { - this.duration = 250; - } - if (this.duration < 0) { - this.loop = true; - this.duration = this.duration * -1; - this.extend = false; - } - - if (this.units == "s") { this.duration = this.duration * 1000; } - if (this.units == "min") { this.duration = this.duration * 1000 * 60; } - if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; } - - this.op1Templated = (this.op1type === 'str' && this.op1.indexOf("{{") != -1); - this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1); - if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); } - if ((this.op2type === "num") && (!isNaN(this.op2))) { this.op2 = Number(this.op2); } - //if (this.op1 == "null") { this.op1 = null; } - //if (this.op2 == "null") { this.op2 = null; } - //try { this.op1 = JSON.parse(this.op1); } - //catch(e) { this.op1 = this.op1; } - //try { this.op2 = JSON.parse(this.op2); } - //catch(e) { this.op2 = this.op2; } - - var node = this; - node.topics = {}; - - var npay = {}; - var pendingMessages = []; - var activeMessagePromise = null; - var processMessageQueue = function(msgInfo) { - if (msgInfo) { - // A new message has arrived - add it to the message queue - pendingMessages.push(msgInfo); - if (activeMessagePromise !== null) { - // The node is currently processing a message, so do nothing - // more with this message - return; - } - } - if (pendingMessages.length === 0) { - // There are no more messages to process, clear the active flag - // and return - activeMessagePromise = null; - return; - } - - // There are more messages to process. Get the next message and - // start processing it. Recurse back in to check for any more - var nextMsgInfo = pendingMessages.shift(); - activeMessagePromise = processMessage(nextMsgInfo) - .then(processMessageQueue) - .catch((err) => { - nextMsgInfo.done(err); - return processMessageQueue(); - }); - } - - this.on('input', function(msg, send, done) { - processMessageQueue({msg, send, done}); - }); - - var stat = function() { - var l = Object.keys(node.topics).length; - if (l === 0) { return {} } - else if (l === 1) { return {fill:"blue",shape:"dot"} } - else return {fill:"blue",shape:"dot",text:l}; - } - - var processMessage = function(msgInfo) { - let msg = msgInfo.msg; - var topic = RED.util.getMessageProperty(msg,node.topic) || "_none"; - var promise; - var delayDuration = node.duration; - if (node.overrideDelay && msg.hasOwnProperty("delay") && !isNaN(parseFloat(msg.delay))) { - delayDuration = parseFloat(msg.delay); - } - if (node.bytopic === "all") { topic = "_none"; } - node.topics[topic] = node.topics[topic] || {}; - if (msg.hasOwnProperty("reset") || ((node.reset !== '') && msg.hasOwnProperty("payload") && (msg.payload !== null) && msg.payload.toString && (msg.payload.toString() == node.reset)) ) { - if (node.loop === true) { clearInterval(node.topics[topic].tout); } - else { clearTimeout(node.topics[topic].tout); } - delete node.topics[topic]; - node.status(stat()); - } - else { - if (node.op2type === "payl") { npay[topic] = RED.util.cloneMessage(msg); } - if (((!node.topics[topic].tout) && (node.topics[topic].tout !== 0)) || (node.loop === true)) { - promise = Promise.resolve(); - if (node.op2type === "pay") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } - else if (node.op2Templated) { node.topics[topic].m2 = mustache.render(node.op2,msg); } - else if (node.op2type !== "nul") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - node.topics[topic].m2 = value; - resolve(); - } - }); - }); - } - - return promise.then(() => { - promise = Promise.resolve(); - if (node.op1type === "pay") { } - else if (node.op1Templated) { msg.payload = mustache.render(node.op1,msg); } - else if (node.op1type !== "nul") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op1,node.op1type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - msg.payload = value; - resolve(); - } - }); - }); - } - return promise.then(() => { - if (delayDuration === 0) { node.topics[topic].tout = 0; } - else if (node.loop === true) { - /* istanbul ignore else */ - if (node.topics[topic].tout) { clearInterval(node.topics[topic].tout); } - /* istanbul ignore else */ - if (node.op1type !== "nul") { - var msg2 = RED.util.cloneMessage(msg); - node.topics[topic].tout = setInterval(function() { - if (node.op1type === "date") { msg2.payload = Date.now(); } - msgInfo.send(RED.util.cloneMessage(msg2)); - }, delayDuration); - } - } - else { - if (!node.topics[topic].tout) { - node.topics[topic].tout = setTimeout(function() { - var msg2 = null; - if (node.op2type !== "nul") { - var promise = Promise.resolve(); - msg2 = RED.util.cloneMessage(msg); - if (node.op2type === "flow" || node.op2type === "global") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - node.topics[topic].m2 = value; - resolve(); - } - }); - }); - } - promise.then(() => { - if (node.op2type === "payl") { - if (node.second === true) { msgInfo.send([null,npay[topic]]); } - else { msgInfo.send(npay[topic]); } - delete npay[topic]; - } - else { - msg2.payload = node.topics[topic].m2; - if (node.op2type === "date") { msg2.payload = Date.now(); } - if (node.second === true) { msgInfo.send([null,msg2]); } - else { msgInfo.send(msg2); } - } - delete node.topics[topic]; - node.status(stat()); - }).catch(err => { - node.error(err); - }); - } else { - delete node.topics[topic]; - node.status(stat()); - } - - }, delayDuration); - } - } - msgInfo.done(); - node.status(stat()); - if (node.op1type !== "nul") { msgInfo.send(RED.util.cloneMessage(msg)); } - }); - }); - } - else if ((node.extend === "true" || node.extend === true) && (delayDuration > 0)) { - /* istanbul ignore else */ - if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } - /* istanbul ignore else */ - if (node.topics[topic].tout) { clearTimeout(node.topics[topic].tout); } - node.topics[topic].tout = setTimeout(function() { - var msg2 = null; - var promise = Promise.resolve(); - - if (node.op2type !== "nul") { - if (node.op2type === "flow" || node.op2type === "global") { - promise = new Promise((resolve,reject) => { - RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { - if (err) { - reject(err); - } else { - node.topics[topic].m2 = value; - resolve(); - } - }); - }); - } - } - promise.then(() => { - if (node.op2type !== "nul") { - if (node.topics[topic] !== undefined) { - msg2 = RED.util.cloneMessage(msg); - msg2.payload = node.topics[topic].m2; - } - } - delete node.topics[topic]; - node.status(stat()); - if (node.second === true) { msgInfo.send([null,msg2]); } - else { msgInfo.send(msg2); } - }).catch(err => { - node.error(err); - }); - }, delayDuration); - } - // else { - // if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } - // } - } - msgInfo.done(); - return Promise.resolve(); - } - this.on("close", function() { - for (var t in node.topics) { - /* istanbul ignore else */ - if (node.topics[t]) { - if (node.loop === true) { clearInterval(node.topics[t].tout); } - else { clearTimeout(node.topics[t].tout); } - delete node.topics[t]; - } - } - node.status(stat()); - }); - } - RED.nodes.registerType("trigger",TriggerNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/90-exec.html b/packages/node_modules/@node-red/nodes/core/function/90-exec.html deleted file mode 100644 index ea988a84a..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/90-exec.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/90-exec.js b/packages/node_modules/@node-red/nodes/core/function/90-exec.js deleted file mode 100644 index cf4168ae8..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/90-exec.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var spawn = require('child_process').spawn; - var exec = require('child_process').exec; - var fs = require('fs'); - var isUtf8 = require('is-utf8'); - - function ExecNode(n) { - RED.nodes.createNode(this,n); - this.cmd = (n.command || "").trim(); - if (n.addpay === undefined) { n.addpay = true; } - this.addpay = n.addpay; - if (this.addpay === true) { - this.addpay = "payload"; - } - this.append = (n.append || "").trim(); - this.useSpawn = (n.useSpawn == "true"); - this.timer = Number(n.timer || 0)*1000; - this.activeProcesses = {}; - this.oldrc = (n.oldrc || false).toString(); - this.execOpt = {encoding:'binary', maxBuffer:RED.settings.execMaxBufferSize||10000000, windowsHide: (n.winHide === true)}; - this.spawnOpt = {windowsHide: (n.winHide === true) } - var node = this; - - if (process.platform === 'linux' && fs.existsSync('/bin/bash')) { node.execOpt.shell = '/bin/bash'; } - - var cleanup = function(p) { - node.activeProcesses[p].kill(); - //node.status({fill:"red",shape:"dot",text:"timeout"}); - //node.error("Exec node timeout"); - } - - this.on("input", function(msg, nodeSend, nodeDone) { - if (msg.hasOwnProperty("kill")) { - if (typeof msg.kill !== "string" || msg.kill.length === 0 || !msg.kill.toUpperCase().startsWith("SIG") ) { msg.kill = "SIGTERM"; } - if (msg.hasOwnProperty("pid")) { - if (node.activeProcesses.hasOwnProperty(msg.pid) ) { - node.activeProcesses[msg.pid].kill(msg.kill.toUpperCase()); - node.status({fill:"red",shape:"dot",text:"killed"}); - } - } - else { - if (Object.keys(node.activeProcesses).length === 1) { - node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill(msg.kill.toUpperCase()); - node.status({fill:"red",shape:"dot",text:"killed"}); - } - } - nodeDone(); - } - else { - var child; - // make the extra args into an array - // then prepend with the msg.payload - var arg = node.cmd; - if (node.addpay) { - var value = RED.util.getMessageProperty(msg, node.addpay); - if (value !== undefined) { - arg += " " + value; - } - } - if (node.append.trim() !== "") { arg += " " + node.append; } - if (this.useSpawn === true) { - // slice whole line by spaces and removes any quotes since spawn can't handle them - arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g).map((a) => { - if (/^".*"$/.test(a)) { - return a.slice(1,-1) - } else { - return a - } - }); - var cmd = arg.shift(); - /* istanbul ignore else */ - if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); } - child = spawn(cmd,arg,node.spawnOpt); - node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid}); - var unknownCommand = (child.pid === undefined); - if (node.timer !== 0) { - child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer); - } - node.activeProcesses[child.pid] = child; - child.stdout.on('data', function (data) { - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - // console.log('[exec] stdout: ' + data,child.pid); - if (isUtf8(data)) { msg.payload = data.toString(); } - else { msg.payload = data; } - nodeSend([RED.util.cloneMessage(msg),null,null]); - } - }); - child.stderr.on('data', function (data) { - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - if (isUtf8(data)) { msg.payload = data.toString(); } - else { msg.payload = Buffer.from(data); } - nodeSend([null,RED.util.cloneMessage(msg),null]); - } - }); - child.on('close', function (code,signal) { - if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) { - delete node.activeProcesses[child.pid]; - if (child.tout) { clearTimeout(child.tout); } - msg.payload = code; - if (node.oldrc === "false") { - msg.payload = {code:code}; - if (signal) { msg.payload.signal = signal; } - } - if (code === 0) { node.status({}); } - if (code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } - else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc:"+code}); } - else { node.status({fill:"yellow",shape:"dot",text:"rc:"+code}); } - nodeSend([null,null,RED.util.cloneMessage(msg)]); - } - nodeDone(); - }); - child.on('error', function (code) { - if (child.tout) { clearTimeout(child.tout); } - delete node.activeProcesses[child.pid]; - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - node.error(code,RED.util.cloneMessage(msg)); - } - }); - } - else { - /* istanbul ignore else */ - if (RED.settings.verbose) { node.log(arg); } - child = exec(arg, node.execOpt, function (error, stdout, stderr) { - var msg2, msg3; - delete msg.payload; - if (stderr) { - msg2 = RED.util.cloneMessage(msg); - msg2.payload = stderr; - } - msg.payload = Buffer.from(stdout,"binary"); - if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); } - node.status({}); - //console.log('[exec] stdout: ' + stdout); - //console.log('[exec] stderr: ' + stderr); - if (error !== null) { - msg3 = RED.util.cloneMessage(msg); - msg3.payload = {code:error.code, message:error.message}; - if (error.signal) { msg3.payload.signal = error.signal; } - if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } - else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); } - if (RED.settings.verbose) { node.log('error:' + error); } - } - else if (node.oldrc === "false") { - msg3 = RED.util.cloneMessage(msg); - msg3.payload = {code:0}; - } - if (!msg3) { node.status({}); } - else { - msg.rc = msg3.payload; - if (msg2) { msg2.rc = msg3.payload; } - } - nodeSend([msg,msg2,msg3]); - if (child.tout) { clearTimeout(child.tout); } - delete node.activeProcesses[child.pid]; - nodeDone(); - }); - node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid}); - child.on('error',function() {}); - if (node.timer !== 0) { - child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer); - } - node.activeProcesses[child.pid] = child; - } - } - }); - - this.on('close',function() { - for (var pid in node.activeProcesses) { - /* istanbul ignore else */ - if (node.activeProcesses.hasOwnProperty(pid)) { - if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); } - // console.log("KILLING",pid); - var process = node.activeProcesses[pid]; - node.activeProcesses[pid] = null; - process.kill(); - } - } - node.activeProcesses = {}; - node.status({}); - }); - } - RED.nodes.registerType("exec",ExecNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/rbe.html b/packages/node_modules/@node-red/nodes/core/function/rbe.html deleted file mode 100644 index 11ff18860..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/rbe.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/05-tls.html b/packages/node_modules/@node-red/nodes/core/network/05-tls.html deleted file mode 100644 index d09860843..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/05-tls.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/05-tls.js b/packages/node_modules/@node-red/nodes/core/network/05-tls.js deleted file mode 100644 index 888d749fd..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/05-tls.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var fs = require('fs'); -module.exports = function(RED) { - "use strict"; - - function TLSConfig(n) { - RED.nodes.createNode(this,n); - this.valid = true; - this.verifyservercert = n.verifyservercert; - var certPath = n.cert.trim(); - var keyPath = n.key.trim(); - var caPath = n.ca.trim(); - this.servername = (n.servername||"").trim(); - this.alpnprotocol = (n.alpnprotocol||"").trim(); - - if ((certPath.length > 0) || (keyPath.length > 0) || (caPath.length > 0)) { - - if ( (certPath.length > 0) !== (keyPath.length > 0)) { - this.valid = false; - this.error(RED._("tls.error.missing-file")); - return; - } - - try { - if (certPath) { - this.cert = fs.readFileSync(certPath); - } - if (keyPath) { - this.key = fs.readFileSync(keyPath); - } - if (caPath) { - this.ca = fs.readFileSync(caPath); - } - } catch(err) { - this.valid = false; - this.error(err.toString()); - return; - } - } else { - if (this.credentials) { - var certData = this.credentials.certdata || ""; - var keyData = this.credentials.keydata || ""; - var caData = this.credentials.cadata || ""; - - if ((certData.length > 0) !== (keyData.length > 0)) { - this.valid = false; - this.error(RED._("tls.error.missing-file")); - return; - } - - if (certData) { - this.cert = certData; - } - if (keyData) { - this.key = keyData; - } - if (caData) { - this.ca = caData; - } - } - } - } - RED.nodes.registerType("tls-config", TLSConfig, { - credentials: { - certdata: {type:"text"}, - keydata: {type:"text"}, - cadata: {type:"text"}, - passphrase: {type:"password"} - }, - settings: { - tlsConfigDisableLocalFiles: { - value: false, - exportable: true - } - } - }); - - TLSConfig.prototype.addTLSOptions = function(opts) { - if (this.valid) { - if (this.key) { - opts.key = this.key; - } - if (this.cert) { - opts.cert = this.cert; - } - if (this.ca) { - opts.ca = this.ca; - } - if (this.credentials && this.credentials.passphrase) { - opts.passphrase = this.credentials.passphrase; - } - if (this.servername) { - opts.servername = this.servername; - } - if (this.alpnprotocol) { - opts.ALPNProtocols = [this.alpnprotocol]; - } - opts.rejectUnauthorized = this.verifyservercert; - } - return opts; - } - -} diff --git a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html b/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html deleted file mode 100644 index 85939a1ef..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js b/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js deleted file mode 100644 index abcee66f6..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - 'use strict'; - - function HTTPProxyConfig(n) { - RED.nodes.createNode(this, n); - this.name = n.name; - this.url = n.url; - this.noproxy = n.noproxy; - }; - - RED.nodes.registerType('http proxy', HTTPProxyConfig, { - credentials: { - username: {type:'text'}, - password: {type:'password'} - } - }); -}; diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html deleted file mode 100644 index 747281ad0..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html +++ /dev/null @@ -1,908 +0,0 @@ - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js deleted file mode 100644 index 5d4efaf93..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js +++ /dev/null @@ -1,1139 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var mqtt = require("mqtt"); - var isUtf8 = require('is-utf8'); - var HttpsProxyAgent = require('https-proxy-agent'); - var url = require('url'); - - //#region "Supporting functions" - function matchTopic(ts,t) { - if (ts == "#") { - return true; - } - /* The following allows shared subscriptions (as in MQTT v5) - http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.html#_Toc514345522 - - 4.8.2 describes shares like: - $share/{ShareName}/{filter} - $share is a literal string that marks the Topic Filter as being a Shared Subscription Topic Filter. - {ShareName} is a character string that does not include "/", "+" or "#" - {filter} The remainder of the string has the same syntax and semantics as a Topic Filter in a non-shared subscription. Refer to section 4.7. - */ - else if(ts.startsWith("$share")){ - ts = ts.replace(/^\$share\/[^#+/]+\/(.*)/g,"$1"); - } - var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); - return re.test(t); - } - - /** - * Helper function for setting integer property values in the MQTT V5 properties object - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {integer} [minVal] The minimum value. If the src value is less than minVal, it will NOT be set in the destination - * @param {integer} [maxVal] The maximum value. If the src value is greater than maxVal, it will NOT be set in the destination - * @param {integer} [def] An optional default to set in the destination object if prop is NOT present in the soruce object - */ - function setIntProp(src, dst, propName, minVal, maxVal, def) { - if (hasProperty(src, propName)) { - var v = parseInt(src[propName]); - if(isNaN(v)) return; - if(minVal != null) { - if(v < minVal) return; - } - if(maxVal != null) { - if(v > maxVal) return; - } - dst[propName] = v; - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Test a topic string is valid - * @param {string} topic - * @returns `true` if it is a valid topic - */ - function isValidSubscriptionTopic(topic) { - return /^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(topic) - } - - /** - * Helper function for setting string property values in the MQTT V5 properties object - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {string} [def] An optional default to set in the destination object if prop is NOT present in the soruce object - */ - function setStrProp(src, dst, propName, def) { - if (src[propName] && typeof src[propName] == "string") { - dst[propName] = src[propName]; - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Helper function for setting boolean property values in the MQTT V5 properties object - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {boolean} [def] An optional default to set in the destination object if prop is NOT present in the soruce object - */ - function setBoolProp(src, dst, propName, def) { - if (src[propName] != null) { - if(src[propName] === "true" || src[propName] === true) { - dst[propName] = true; - } else if(src[propName] === "false" || src[propName] === false) { - dst[propName] = true; - } - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Helper function for copying the MQTT v5 srcUserProperties object (parameter1) to the properties object (parameter2). - * Any property in srcUserProperties that is NOT a key/string pair will be silently discarded. - * NOTE: if no sutable properties are present, the userProperties object will NOT be added to the properties object - * @param {object} srcUserProperties An object with key/value string pairs - * @param {object} properties A properties object in which userProperties will be copied to - */ - function setUserProperties(srcUserProperties, properties) { - if (srcUserProperties && typeof srcUserProperties == "object") { - let _clone = {}; - let count = 0; - let keys = Object.keys(srcUserProperties); - if(!keys || !keys.length) return null; - keys.forEach(key => { - let val = srcUserProperties[key]; - if(typeof val == "string") { - count++; - _clone[key] = val; - } - }); - if(count) properties.userProperties = _clone; - } - } - - /** - * Helper function for copying the MQTT v5 buffer type properties - * NOTE: if src[propName] is not a buffer, dst[propName] will NOT be assigned a value (unless def is set) - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set/add properties - * @param {string} propName The property name to set in the Destination object - * @param {boolean} [def] An optional default to set in the destination object if prop is NOT present in the Source object - */ - function setBufferProp(src, dst, propName, def) { - if(!dst) return; - if (src && dst) { - var buf = src[propName]; - if (buf && typeof Buffer.isBuffer(buf)) { - dst[propName] = Buffer.from(buf); - } - } else { - if(def != undefined) dst[propName] = def; - } - } - - /** - * Helper function for applying changes to an objects properties ONLY when the src object actually has the property. - * This avoids setting a `dst` property null/undefined when the `src` object doesnt have the named property. - * @param {object} src Source object containing properties - * @param {object} dst Destination object to set property - * @param {string} propName The property name to set in the Destination object - * @param {boolean} force force the dst property to be updated/created even if src property is empty - */ - function setIfHasProperty(src, dst, propName, force) { - if (src && dst && propName) { - const ok = force || hasProperty(src, propName); - if (ok) { - dst[propName] = src[propName]; - } - } - } - - /** - * Helper function to test an object has a property - * @param {object} obj Object to test - * @param {string} propName Name of property to find - * @returns true if object has property `propName` - */ - function hasProperty(obj, propName) { - //JavaScript does not protect the property name hasOwnProperty - //Object.prototype.hasOwnProperty.call is the recommended/safer test - return Object.prototype.hasOwnProperty.call(obj, propName); - } - - /** - * Handle the payload / packet recieved in MQTT In and MQTT Sub nodes - */ - function subscriptionHandler(node, datatype ,topic, payload, packet) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - - if (datatype === "buffer") { - // payload = payload; - } else if (datatype === "base64") { - payload = payload.toString('base64'); - } else if (datatype === "utf8") { - payload = payload.toString('utf8'); - } else if (datatype === "json") { - if (isUtf8(payload)) { - payload = payload.toString(); - try { payload = JSON.parse(payload); } - catch(e) { node.error(RED._("mqtt.errors.invalid-json-parse"),{payload:payload, topic:topic, qos:packet.qos, retain:packet.retain}); return; } - } - else { node.error((RED._("mqtt.errors.invalid-json-string")),{payload:payload, topic:topic, qos:packet.qos, retain:packet.retain}); return; } - } else { - if (isUtf8(payload)) { payload = payload.toString(); } - } - var msg = {topic:topic, payload:payload, qos:packet.qos, retain:packet.retain}; - if(v5 && packet.properties) { - setStrProp(packet.properties, msg, "responseTopic"); - setBufferProp(packet.properties, msg, "correlationData"); - setStrProp(packet.properties, msg, "contentType"); - setIntProp(packet.properties, msg, "messageExpiryInterval", 0); - setBoolProp(packet.properties, msg, "payloadFormatIndicator"); - setStrProp(packet.properties, msg, "reasonString"); - setUserProperties(packet.properties.userProperties, msg); - } - if ((node.brokerConn.broker === "localhost")||(node.brokerConn.broker === "127.0.0.1")) { - msg._topic = topic; - } - node.send(msg); - } - - /** - * Send an mqtt message to broker - * @param {MQTTOutNode} node the owner node - * @param {object} msg The msg to prepare for publishing - * @param {function} done callback when done - */ - function doPublish(node, msg, done) { - try { - done = typeof done == "function" ? done : function noop(){}; - let v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - const bsp = (node.brokerConn && node.brokerConn.serverProperties) || {}; - - //Sanitise the `msg` object properties ready for publishing - if (msg.qos) { - msg.qos = parseInt(msg.qos); - if ((msg.qos !== 0) && (msg.qos !== 1) && (msg.qos !== 2)) { - msg.qos = null; - } - } - - /* If node properties exists, override/set that to property in msg */ - if (node.topic) { msg.topic = node.topic; } - msg.qos = Number(node.qos || msg.qos || 0); - msg.retain = node.retain || msg.retain || false; - msg.retain = ((msg.retain === true) || (msg.retain === "true")) || false; - - if (v5) { - if (node.userProperties) { - msg.userProperties = node.userProperties; - } - if (node.responseTopic) { - msg.responseTopic = node.responseTopic; - } - if (node.correlationData) { - msg.correlationData = node.correlationData; - } - if (node.contentType) { - msg.contentType = node.contentType; - } - if (node.messageExpiryInterval) { - msg.messageExpiryInterval = node.messageExpiryInterval; - } - } - if (msg.userProperties && typeof msg.userProperties !== "object") { - delete msg.userProperties; - } - if (hasProperty(msg, "topicAlias") && !isNaN(msg.topicAlias) && (msg.topicAlias === 0 || bsp.topicAliasMaximum === 0 || msg.topicAlias > bsp.topicAliasMaximum)) { - delete msg.topicAlias; - } - - if (hasProperty(msg, "payload")) { - - //check & sanitise topic - let topicOK = hasProperty(msg, "topic") && (typeof msg.topic === "string") && (msg.topic !== ""); - - if (!topicOK && v5) { - //NOTE: A value of 0 (in server props topicAliasMaximum) indicates that the Server does not accept any Topic Aliases on this connection - if (hasProperty(msg, "topicAlias") && !isNaN(msg.topicAlias) && msg.topicAlias >= 0 && bsp.topicAliasMaximum && bsp.topicAliasMaximum >= msg.topicAlias) { - topicOK = true; - msg.topic = ""; //must be empty string - } else if (hasProperty(msg, "responseTopic") && (typeof msg.responseTopic === "string") && (msg.responseTopic !== "")) { - //TODO: if topic is empty but responseTopic has a string value, use that instead. Is this desirable? - topicOK = true; - msg.topic = msg.responseTopic; - //TODO: delete msg.responseTopic - to prevent it being resent? - } - } - topicOK = topicOK && !/[\+#]/.test(msg.topic); - - if (topicOK) { - node.brokerConn.publish(msg, done); // send the message - } else { - node.warn(RED._("mqtt.errors.invalid-topic")); - done(); - } - } else { - done(); - } - } catch (error) { - done(error); - } - } - - function setStatusDisconnected(node, allNodes) { - if(allNodes) { - for (var id in node.users) { - if (hasProperty(node.users, id)) { - node.users[id].status({ fill: "red", shape: "ring", text: "node-red:common.status.disconnected" }); - } - } - } else { - node.status({ fill: "red", shape: "ring", text: "node-red:common.status.disconnected" }); - } - } - - function setStatusConnecting(node, allNodes) { - if(allNodes) { - for (var id in node.users) { - if (hasProperty(node.users, id)) { - node.users[id].status({ fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" }); - } - } - } else { - node.status({ fill: "yellow", shape: "ring", text: "node-red:common.status.connecting" }); - } - } - - function setStatusConnected(node, allNodes) { - if(allNodes) { - for (var id in node.users) { - if (hasProperty(node.users, id)) { - node.users[id].status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" }); - } - } - } else { - node.status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" }); - } - } - - function handleConnectAction(node, msg, done) { - let actionData = typeof msg.broker === 'object' ? msg.broker : null; - if (node.brokerConn.canConnect()) { - // Not currently connected/connecting - trigger the connect - if (actionData) { - node.brokerConn.setOptions(actionData); - } - node.brokerConn.connect(function () { - done(); - }); - } else { - // Already Connected/Connecting - if (!actionData) { - // All is good - already connected and no broker override provided - done() - } else if (actionData.force) { - // The force flag tells us to cycle the connection. - node.brokerConn.disconnect(function() { - node.brokerConn.setOptions(actionData); - node.brokerConn.connect(function () { - done(); - }); - }) - } else { - // Without force flag, we will refuse to cycle an active connection - done(new Error(RED._('mqtt.errors.invalid-action-alreadyconnected'))); - } - } - } - - function handleDisconnectAction(node, done) { - node.brokerConn.disconnect(function () { - done(); - }); - } - - //#endregion "Supporting functions" - - //#region "Broker node" - function MQTTBrokerNode(n) { - RED.nodes.createNode(this,n); - const node = this; - node.users = {}; - // Config node state - node.brokerurl = ""; - node.connected = false; - node.connecting = false; - node.closing = false; - node.options = {}; - node.queue = []; - node.subscriptions = {}; - /** @type {mqtt.MqttClient}*/ this.client; - node.setOptions = function(opts, init) { - if(!opts || typeof opts !== "object") { - return; //nothing to change, simply return - } - const originalBrokerURL = node.brokerurl; - - //apply property changes (only if the property exists in the opts object) - setIfHasProperty(opts, node, "url", init); - setIfHasProperty(opts, node, "broker", init); - setIfHasProperty(opts, node, "port", init); - setIfHasProperty(opts, node, "clientid", init); - setIfHasProperty(opts, node, "autoConnect", init); - setIfHasProperty(opts, node, "usetls", init); - setIfHasProperty(opts, node, "usews", init); - setIfHasProperty(opts, node, "verifyservercert", init); - setIfHasProperty(opts, node, "compatmode", init); - setIfHasProperty(opts, node, "protocolVersion", init); - setIfHasProperty(opts, node, "keepalive", init); - setIfHasProperty(opts, node, "cleansession", init); - setIfHasProperty(opts, node, "sessionExpiry", init); - setIfHasProperty(opts, node, "topicAliasMaximum", init); - setIfHasProperty(opts, node, "maximumPacketSize", init); - setIfHasProperty(opts, node, "receiveMaximum", init); - setIfHasProperty(opts, node, "userProperties", init);//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116 - setIfHasProperty(opts, node, "userPropertiesType", init); - - function createLWT(topic, payload, qos, retain, v5opts, v5SubPropName) { - let message = undefined; - if(topic) { - message = { - topic: topic, - payload: payload || "", - qos: Number(qos||0), - retain: retain=="true"|| retain===true, - } - if (v5opts) { - let v5Properties = message; - if(v5SubPropName) { - v5Properties = message[v5SubPropName] = {}; - } - //re-align local prop name to mqttjs std - if(hasProperty(v5opts, "respTopic")) { v5opts.responseTopic = v5opts.respTopic; } - if(hasProperty(v5opts, "correl")) { v5opts.correlationData = v5opts.correl; } - if(hasProperty(v5opts, "expiry")) { v5opts.messageExpiryInterval = v5opts.expiry; } - if(hasProperty(v5opts, "delay")) { v5opts.willDelayInterval = v5opts.delay; } - if(hasProperty(v5opts, "userProps")) { v5opts.userProperties = v5opts.userProps; } - //setup v5 properties - if(typeof v5opts.userProperties == "string" && /^ *{/.test(v5opts.userProperties)) { - try { - setUserProperties(JSON.parse(v5opts.userProps), v5Properties); - } catch(err) {} - } else if (typeof v5opts.userProperties == "object") { - setUserProperties(v5opts.userProperties, v5Properties); - } - setStrProp(v5opts, v5Properties, "contentType"); - setStrProp(v5opts, v5Properties, "responseTopic"); - setBufferProp(v5opts, v5Properties, "correlationData"); - setIntProp(v5opts, v5Properties, "messageExpiryInterval"); - setIntProp(v5opts, v5Properties, "willDelayInterval"); - } - } - return message; - } - - if(init) { - if(hasProperty(opts, "birthTopic")) { - node.birthMessage = createLWT(opts.birthTopic, opts.birthPayload, opts.birthQos, opts.birthRetain, opts.birthMsg, ""); - }; - if(hasProperty(opts, "closeTopic")) { - node.closeMessage = createLWT(opts.closeTopic, opts.closePayload, opts.closeQos, opts.closeRetain, opts.closeMsg, ""); - }; - if(hasProperty(opts, "willTopic")) { - //will v5 properties must be set in the "properties" sub object - node.options.will = createLWT(opts.willTopic, opts.willPayload, opts.willQos, opts.willRetain, opts.willMsg, "properies"); - }; - } else { - //update options - if(hasProperty(opts, "birth")) { - if(typeof opts.birth !== "object") { opts.birth = {}; } - node.birthMessage = createLWT(opts.birth.topic, opts.birth.payload, opts.birth.qos, opts.birth.retain, opts.birth.properties, ""); - } - if(hasProperty(opts, "close")) { - if(typeof opts.close !== "object") { opts.close = {}; } - node.closeMessage = createLWT(opts.close.topic, opts.close.payload, opts.close.qos, opts.close.retain, opts.close.properties, ""); - } - if(hasProperty(opts, "will")) { - if(typeof opts.will !== "object") { opts.will = {}; } - //will v5 properties must be set in the "properties" sub object - node.options.will = createLWT(opts.will.topic, opts.will.payload, opts.will.qos, opts.will.retain, opts.will.properties, "properties"); - } - } - - if (node.credentials) { - node.username = node.credentials.user; - node.password = node.credentials.password; - } - if(!init & hasProperty(opts, "username")) { - node.username = opts.username; - }; - if(!init & hasProperty(opts, "password")) { - node.password = opts.password; - }; - - // If the config node is missing certain options (it was probably deployed prior to an update to the node code), - // select/generate sensible options for the new fields - if (typeof node.usetls === 'undefined') { - node.usetls = false; - } - if (typeof node.usews === 'undefined') { - node.usews = false; - } - if (typeof node.verifyservercert === 'undefined') { - node.verifyservercert = false; - } - if (typeof node.keepalive === 'undefined') { - node.keepalive = 60; - } else if (typeof node.keepalive === 'string') { - node.keepalive = Number(node.keepalive); - } - if (typeof node.cleansession === 'undefined') { - node.cleansession = true; - } - - //use url or build a url from usetls://broker:port - if (node.url && node.brokerurl !== node.url) { - node.brokerurl = node.url; - } else { - // if the broker is ws:// or wss:// or tcp:// - if (node.broker.indexOf("://") > -1) { - node.brokerurl = node.broker; - // Only for ws or wss, check if proxy env var for additional configuration - if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) { - // check if proxy is set in env - let prox, noprox; - if (process.env.http_proxy) { prox = process.env.http_proxy; } - if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } - if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } - if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } - if (noprox) { - for (var i = 0; i < noprox.length; i += 1) { - if (node.brokerurl.indexOf(noprox[i].trim()) !== -1) { noproxy = true; } - } - } - if (prox && !noproxy) { - var parsedUrl = url.parse(node.brokerurl); - var proxyOpts = url.parse(prox); - // true for wss - proxyOpts.secureEndpoint = parsedUrl.protocol ? parsedUrl.protocol === 'wss:' : true; - // Set Agent for wsOption in MQTT - var agent = new HttpsProxyAgent(proxyOpts); - node.options.wsOptions = { - agent: agent - }; - } - } - } else { - // construct the std mqtt:// url - if (node.usetls) { - node.brokerurl = "mqtts://"; - } else { - node.brokerurl = "mqtt://"; - } - if (node.broker !== "") { - //Check for an IPv6 address - if (/(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)/.test(node.broker)) { - node.brokerurl = node.brokerurl + "[" + node.broker + "]:"; - } else { - node.brokerurl = node.brokerurl + node.broker + ":"; - } - // port now defaults to 1883 if unset. - if (!node.port) { - node.brokerurl = node.brokerurl + "1883"; - } else { - node.brokerurl = node.brokerurl + node.port; - } - } else { - node.brokerurl = node.brokerurl + "localhost:1883"; - } - } - } - - // Ensure cleansession set if clientid not supplied - if (!node.cleansession && !node.clientid) { - node.cleansession = true; - node.warn(RED._("mqtt.errors.nonclean-missingclientid")); - } - - // Build options for passing to the MQTT.js API - node.options.username = node.username; - node.options.password = node.password; - node.options.keepalive = node.keepalive; - node.options.clean = node.cleansession; - node.options.clientId = node.clientid || 'nodered_' + RED.util.generateId(); - node.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000; - delete node.options.protocolId; //V4+ default - delete node.options.protocolVersion; //V4 default - delete node.options.properties;//V5 only - if (node.compatmode == "true" || node.compatmode === true || node.protocolVersion == 3) { - node.options.protocolId = 'MQIsdp';//V3 compat only - node.options.protocolVersion = 3; - } else if ( node.protocolVersion == 5 ) { - delete node.options.protocolId; - node.options.protocolVersion = 5; - node.options.properties = {}; - node.options.properties.requestResponseInformation = true; - node.options.properties.requestProblemInformation = true; - if(node.userProperties && /^ *{/.test(node.userProperties)) { - try { - setUserProperties(JSON.parse(node.userProperties), node.options.properties); - } catch(err) {} - } - if (node.sessionExpiryInterval && node.sessionExpiryInterval !== "0") { - setIntProp(node,node.options.properties,"sessionExpiryInterval"); - } - } - if (node.usetls && n.tls) { - var tlsNode = RED.nodes.getNode(n.tls); - if (tlsNode) { - tlsNode.addTLSOptions(node.options); - } - } - - // If there's no rejectUnauthorized already, then this could be an - // old config where this option was provided on the broker node and - // not the tls node - if (typeof node.options.rejectUnauthorized === 'undefined') { - node.options.rejectUnauthorized = (node.verifyservercert == "true" || node.verifyservercert === true); - } - } - - n.autoConnect = n.autoConnect === "false" || n.autoConnect === false ? false : true; - node.setOptions(n, true); - - // Define functions called by MQTT in and out nodes - node.register = function(mqttNode) { - node.users[mqttNode.id] = mqttNode; - if (Object.keys(node.users).length === 1) { - if(node.autoConnect) { - node.connect(); - } - } - }; - - node.deregister = function(mqttNode,done) { - delete node.users[mqttNode.id]; - if (node.closing) { - return done(); - } - if (Object.keys(node.users).length === 0) { - if (node.client && node.client.connected) { - // Send close message - if (node.closeMessage) { - node.publish(node.closeMessage,function(err) { - node.client.end(done); - }); - } else { - node.client.end(done); - } - return; - } else { - if (node.client) { node.client.end(); } - return done(); - } - } - done(); - }; - node.canConnect = function() { - return !node.connected && !node.connecting; - } - node.connect = function (callback) { - if (node.canConnect()) { - node.connecting = true; - setStatusConnecting(node, true); - try { - node.serverProperties = {}; - node.client = mqtt.connect(node.brokerurl, node.options); - node.client.setMaxListeners(0); - let callbackDone = false; //prevent re-connects causing node.client.on('connect' firing callback multiple times - // Register successful connect or reconnect handler - node.client.on('connect', function (connack) { - node.connecting = false; - node.connected = true; - if(!callbackDone && typeof callback == "function") { - callback(); - } - callbackDone = true; - node.topicAliases = {}; - node.log(RED._("mqtt.state.connected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl})); - if(node.options.protocolVersion == 5 && connack && hasProperty(connack, "properties")) { - if(typeof connack.properties == "object") { - //clean & assign all props sent from server. - setIntProp(connack.properties, node.serverProperties, "topicAliasMaximum", 0); - setIntProp(connack.properties, node.serverProperties, "receiveMaximum", 0); - setIntProp(connack.properties, node.serverProperties, "sessionExpiryInterval", 0, 0xFFFFFFFF); - setIntProp(connack.properties, node.serverProperties, "maximumQoS", 0, 2); - setBoolProp(connack.properties, node.serverProperties, "retainAvailable",true); - setBoolProp(connack.properties, node.serverProperties, "wildcardSubscriptionAvailable", true); - setBoolProp(connack.properties, node.serverProperties, "subscriptionIdentifiersAvailable", true); - setBoolProp(connack.properties, node.serverProperties, "sharedSubscriptionAvailable"); - setIntProp(connack.properties, node.serverProperties, "maximumPacketSize", 0); - setIntProp(connack.properties, node.serverProperties, "serverKeepAlive"); - setStrProp(connack.properties, node.serverProperties, "responseInformation"); - setStrProp(connack.properties, node.serverProperties, "serverReference"); - setStrProp(connack.properties, node.serverProperties, "assignedClientIdentifier"); - setStrProp(connack.properties, node.serverProperties, "reasonString"); - setUserProperties(connack.properties, node.serverProperties); - } - } - setStatusConnected(node, true); - // Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection - node.client.removeAllListeners('message'); - - // Re-subscribe to stored topics - for (var s in node.subscriptions) { - if (node.subscriptions.hasOwnProperty(s)) { - let topic = s; - let qos = 0; - let _options = {}; - for (var r in node.subscriptions[s]) { - if (node.subscriptions[s].hasOwnProperty(r)) { - qos = Math.max(qos,node.subscriptions[s][r].qos); - _options = node.subscriptions[s][r].options; - node.client.on('message',node.subscriptions[s][r].handler); - } - } - _options.qos = _options.qos || qos; - node.client.subscribe(topic, _options); - } - } - - // Send any birth message - if (node.birthMessage) { - node.publish(node.birthMessage); - } - }); - node.client.on("reconnect", function() { - setStatusConnecting(node, true); - }); - //TODO: what to do with this event? Anything? Necessary? - node.client.on("disconnect", function(packet) { - //Emitted after receiving disconnect packet from broker. MQTT 5.0 feature. - var rc = packet && packet.properties && packet.properties.reasonString; - var rc = packet && packet.properties && packet.reasonCode; - //TODO: If keeping this event, do we use these? log these? - }); - // Register disconnect handlers - node.client.on('close', function () { - if (node.connected) { - node.connected = false; - node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl})); - setStatusDisconnected(node, true); - } else if (node.connecting) { - node.log(RED._("mqtt.state.connect-failed",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl})); - } - }); - - // Register connect error handler - // The client's own reconnect logic will take care of errors - node.client.on('error', function (error) { - }); - }catch(err) { - console.log(err); - } - } - }; - node.disconnect = function (callback) { - const _callback = function () { - setStatusDisconnected(node, true); - node.connecting = false; - node.connected = false; - callback && typeof callback == "function" && callback(); - }; - - if(node.client) { - if(node.client.connected && node.closeMessage) { - node.publish(node.closeMessage, function (err) { - node.client.end(_callback); - }); - } else if(node.client.connected || node.client.reconnecting) { - node.client.end(_callback); - } else if(node.client.disconnecting || node.client.connected === false) { - _callback(); - } - } else { - _callback(); - } - } - node.subscriptionIds = {}; - node.subid = 1; - node.subscribe = function (topic,options,callback,ref) { - ref = ref||0; - var qos; - if(typeof options == "object") { - qos = options.qos; - } else { - qos = options; - options = {}; - } - options.qos = qos; - if (!node.subscriptionIds[topic]) { - node.subscriptionIds[topic] = node.subid++; - } - options.properties = options.properties || {}; - options.properties.subscriptionIdentifier = node.subscriptionIds[topic]; - - node.subscriptions[topic] = node.subscriptions[topic]||{}; - var sub = { - topic:topic, - qos:qos, - options:options, - handler:function(mtopic,mpayload, mpacket) { - if(mpacket.properties && options.properties && mpacket.properties.subscriptionIdentifier && options.properties.subscriptionIdentifier && (mpacket.properties.subscriptionIdentifier !== options.properties.subscriptionIdentifier) ) { - //do nothing as subscriptionIdentifier does not match - } else if (matchTopic(topic,mtopic)) { - callback(mtopic,mpayload, mpacket); - } - }, - ref: ref - }; - node.subscriptions[topic][ref] = sub; - if (node.connected) { - node.client.on('message',sub.handler); - node.client.subscribe(topic, options); - } - }; - - node.unsubscribe = function (topic, ref, removed) { - ref = ref||0; - var sub = node.subscriptions[topic]; - if (sub) { - if (sub[ref]) { - if(node.client) { - node.client.removeListener('message',sub[ref].handler); - } - delete sub[ref]; - } - //TODO: Review. The `if(removed)` was commented out to always delete and remove subscriptions. - // if we dont then property changes dont get applied and old subs still trigger - //if (removed) { - if (Object.keys(sub).length === 0) { - delete node.subscriptions[topic]; - delete node.subscriptionIds[topic]; - if (node.connected) { - node.client.unsubscribe(topic); - } - } - //} - } - }; - node.topicAliases = {}; - - node.publish = function (msg,done) { - if (node.connected) { - if (msg.payload === null || msg.payload === undefined) { - msg.payload = ""; - } else if (!Buffer.isBuffer(msg.payload)) { - if (typeof msg.payload === "object") { - msg.payload = JSON.stringify(msg.payload); - } else if (typeof msg.payload !== "string") { - msg.payload = "" + msg.payload; - } - } - var options = { - qos: msg.qos || 0, - retain: msg.retain || false - }; - //https://github.com/mqttjs/MQTT.js/blob/master/README.md#mqttclientpublishtopic-message-options-callback - if(node.options.protocolVersion == 5) { - options.properties = options.properties || {}; - setStrProp(msg, options.properties, "responseTopic"); - setBufferProp(msg, options.properties, "correlationData"); - setStrProp(msg, options.properties, "contentType"); - setIntProp(msg, options.properties, "messageExpiryInterval", 0); - setUserProperties(msg.userProperties, options.properties); - setIntProp(msg, options.properties, "topicAlias", 1, node.serverProperties.topicAliasMaximum || 0); - setBoolProp(msg, options.properties, "payloadFormatIndicator"); - //FUTURE setIntProp(msg, options.properties, "subscriptionIdentifier", 1, 268435455); - if (options.properties.topicAlias) { - if (!node.topicAliases.hasOwnProperty(options.properties.topicAlias) && msg.topic == "") { - done("Invalid topicAlias"); - return - } - if (node.topicAliases[options.properties.topicAlias] === msg.topic) { - msg.topic = "" - } else { - node.topicAliases[options.properties.topicAlias] = msg.topic - } - } - } - - node.client.publish(msg.topic, msg.payload, options, function(err) { - done && done(err); - return - }); - } - }; - - node.on('close', function(done) { - node.closing = true; - node.disconnect(done); - }); - - } - - RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{ - credentials: { - user: {type:"text"}, - password: {type: "password"} - } - }); - //#endregion "Broker node" - - //#region "MQTTIn node" - function MQTTInNode(n) { - RED.nodes.createNode(this,n); - /**@type {MQTTInNode}*/const node = this; - /**@type {string}*/node.broker = n.broker; - /**@type {MQTTBrokerNode}*/node.brokerConn = RED.nodes.getNode(node.broker); - - node.dynamicSubs = {}; - node.isDynamic = n.hasOwnProperty("inputs") && n.inputs == 1 - node.inputs = n.inputs; - node.topic = n.topic; - node.qos = parseInt(n.qos); - node.subscriptionIdentifier = n.subscriptionIdentifier;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117 - node.nl = n.nl; - node.rap = n.rap; - node.rh = n.rh; - - const Actions = { - CONNECT: 'connect', - DISCONNECT: 'disconnect', - SUBSCRIBE: 'subscribe', - UNSUBSCRIBE: 'unsubscribe', - GETSUBS: 'getSubscriptions', - }; - const allowableActions = Object.values(Actions); - - if (isNaN(node.qos) || node.qos < 0 || node.qos > 2) { - node.qos = 2; - } - if (!node.isDynamic && !isValidSubscriptionTopic(node.topic)) { - return node.warn(RED._("mqtt.errors.invalid-topic")); - } - node.datatype = n.datatype || "utf8"; - if (node.brokerConn) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - setStatusDisconnected(node); - if (node.topic || node.isDynamic) { - node.brokerConn.register(node); - if (!node.isDynamic) { - let options = { qos: node.qos }; - if(v5) { - setIntProp(node, options, "rh", 0, 2, 0); - if(node.nl === "true" || node.nl === true) options.nl = true; - else if(node.nl === "false" || node.nl === false) options.nl = false; - if(node.rap === "true" || node.rap === true) options.rap = true; - else if(node.rap === "false" || node.rap === false) options.rap = false; - } - - node.brokerConn.subscribe(node.topic,options,function(topic, payload, packet) { - subscriptionHandler(node, node.datatype, topic, payload, packet); - },node.id); - } - if (node.brokerConn.connected) { - node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"}); - } - } - else { - node.error(RED._("mqtt.errors.not-defined")); - } - node.on('input', function (msg, send, done) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - const action = msg.action; - - if (!allowableActions.includes(action)) { - done(new Error(RED._('mqtt.errors.invalid-action-action'))); - return; - } - - if (action === Actions.CONNECT) { - handleConnectAction(node, msg, done) - } else if (action === Actions.DISCONNECT) { - handleDisconnectAction(node, done) - } else if (action === Actions.SUBSCRIBE || action === Actions.UNSUBSCRIBE) { - const subscriptions = []; - let actionData; - //coerce msg.topic into an array of strings or objects (for later iteration) - if(action === Actions.UNSUBSCRIBE && msg.topic === true) { - actionData = Object.values(node.dynamicSubs); - } else if (Array.isArray(msg.topic)) { - actionData = msg.topic; - } else if (typeof msg.topic == 'string' || typeof msg.topic == 'object') { - actionData = [msg.topic]; - } else { - done(new Error(RED._('mqtt.errors.invalid-action-badsubscription'))); - return; - } - //ensure each subscription is an object with topic etc - for (let index = 0; index < actionData.length; index++) { - let subscription = actionData[index]; - if (typeof subscription === 'string') { - subscription = { topic: subscription }; - } - if (!subscription.topic || !isValidSubscriptionTopic(subscription.topic)) { - done(new Error(RED._('mqtt.errors.invalid-topic'))); - return; - } - subscriptions.push(subscription); - } - if (action === Actions.UNSUBSCRIBE) { - subscriptions.forEach(function (sub) { - node.brokerConn.unsubscribe(sub.topic, node.id); - delete node.dynamicSubs[sub.topic]; - }) - //user can access current subscriptions through the complete node is so desired - msg.subscriptions = Object.values(node.dynamicSubs); - done(); - } else if (action === Actions.SUBSCRIBE) { - subscriptions.forEach(function (sub) { - //always unsubscribe before subscribe to prevent multiple subs to same topic - if (node.dynamicSubs[sub.topic]) { - node.brokerConn.unsubscribe(sub.topic, node.id); - delete node.dynamicSubs[sub.topic]; - } - - //prepare options. Default qos 2 & rap flag true (same as 'mqtt in' node ui defaults when adding to editor) - let options = {} - setIntProp(sub, options, 'qos', 0, 2, 2);//default to qos 2 (same as 'mqtt in' default) - sub.qos = options.qos; - if (v5) { - setIntProp(sub, options, 'rh', 0, 2, 0); //default rh to 0:send retained messages (same as 'mqtt in' default) - sub.rh = options.rh; - setBoolProp(sub, options, 'rap', true); //default rap to true:Keep retain flag of original publish (same as 'mqtt in' default) - sub.rap = options.rap; - if (sub.nl === 'true' || sub.nl === true) { - options.nl = true; - sub.nl = true; - } else if (sub.nl === 'false' || sub.nl === false) { - options.nl = false; - sub.nl = false; - } else { - delete sub.nl - } - } - - //subscribe to sub.topic & hook up subscriptionHandler - node.brokerConn.subscribe(sub.topic, options, function (topic, payload, packet) { - subscriptionHandler(node, sub.datatype || node.datatype, topic, payload, packet); - }, node.id); - node.dynamicSubs[sub.topic] = sub; //save for later unsubscription & 'list' action - }) - //user can access current subscriptions through the complete node is so desired - msg.subscriptions = Object.values(node.dynamicSubs); - done(); - } - } else if (action === Actions.GETSUBS) { - //send list of subscriptions in payload - msg.topic = "subscriptions"; - msg.payload = Object.values(node.dynamicSubs); - send(msg); - done(); - } - }); - - node.on('close', function(removed, done) { - if (node.brokerConn) { - if(node.isDynamic) { - Object.keys(node.dynamicSubs).forEach(function (topic) { - node.brokerConn.unsubscribe(topic, node.id, removed); - }); - node.dynamicSubs = {}; - } else { - node.brokerConn.unsubscribe(node.topic,node.id, removed); - } - node.brokerConn.deregister(node, done); - } - }); - } else { - node.error(RED._("mqtt.errors.missing-config")); - } - } - RED.nodes.registerType("mqtt in",MQTTInNode); - //#endregion "MQTTIn node" - - //#region "MQTTOut node" - function MQTTOutNode(n) { - RED.nodes.createNode(this,n); - const node = this; - node.topic = n.topic; - node.qos = n.qos || null; - node.retain = n.retain; - node.broker = n.broker; - node.responseTopic = n.respTopic;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901114 - node.correlationData = n.correl;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901115 - node.contentType = n.contentType;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901118 - node.messageExpiryInterval = n.expiry; //https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901112 - try { - if (/^ *{/.test(n.userProps)) { - //setup this.userProperties - setUserProperties(JSON.parse(n.userProps), node);//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116 - } - } catch(err) {} - // node.topicAlias = n.topicAlias; //https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901113 - // node.payloadFormatIndicator = n.payloadFormatIndicator; //https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111 - // node.subscriptionIdentifier = n.subscriptionIdentifier;//https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117 - - /** @type {MQTTBrokerNode}*/node.brokerConn = RED.nodes.getNode(node.broker); - - const Actions = { - CONNECT: 'connect', - DISCONNECT: 'disconnect', - }; - - if (node.brokerConn) { - setStatusDisconnected(node); - node.on("input",function(msg,send,done) { - if (msg.action) { - if (msg.action === Actions.CONNECT) { - handleConnectAction(node, msg, done) - } else if (msg.action === Actions.DISCONNECT) { - handleDisconnectAction(node, done) - } else { - done(new Error(RED._('mqtt.errors.invalid-action-action'))); - return; - } - } else { - doPublish(node, msg, done); - } - - }); - if (node.brokerConn.connected) { - node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"}); - } - node.brokerConn.register(node); - node.on('close', function(done) { - node.brokerConn.deregister(node,done); - }); - } else { - node.error(RED._("mqtt.errors.missing-config")); - } - } - RED.nodes.registerType("mqtt out",MQTTOutNode); - //#endregion "MQTTOut node" -}; diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httpin.html b/packages/node_modules/@node-red/nodes/core/network/21-httpin.html deleted file mode 100644 index 450bd32cc..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httpin.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httpin.js b/packages/node_modules/@node-red/nodes/core/network/21-httpin.js deleted file mode 100644 index b458a459c..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httpin.js +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var bodyParser = require("body-parser"); - var multer = require("multer"); - var cookieParser = require("cookie-parser"); - var getBody = require('raw-body'); - var cors = require('cors'); - var onHeaders = require('on-headers'); - var typer = require('content-type'); - var mediaTyper = require('media-typer'); - var isUtf8 = require('is-utf8'); - var hashSum = require("hash-sum"); - - function rawBodyParser(req, res, next) { - if (req.skipRawBodyParser) { next(); } // don't parse this if told to skip - if (req._body) { return next(); } - req.body = ""; - req._body = true; - - var isText = true; - var checkUTF = false; - - if (req.headers['content-type']) { - var contentType = typer.parse(req.headers['content-type']) - if (contentType.type) { - var parsedType = mediaTyper.parse(contentType.type); - if (parsedType.type === "text") { - isText = true; - } else if (parsedType.subtype === "xml" || parsedType.suffix === "xml") { - isText = true; - } else if (parsedType.type !== "application") { - isText = false; - } else if ((parsedType.subtype !== "octet-stream") - && (parsedType.subtype !== "cbor") - && (parsedType.subtype !== "x-protobuf")) { - checkUTF = true; - } else { - // application/octet-stream or application/cbor - isText = false; - } - - } - } - - getBody(req, { - length: req.headers['content-length'], - encoding: isText ? "utf8" : null - }, function (err, buf) { - if (err) { return next(err); } - if (!isText && checkUTF && isUtf8(buf)) { - buf = buf.toString() - } - req.body = buf; - next(); - }); - } - - var corsSetup = false; - - function createRequestWrapper(node,req) { - // This misses a bunch of properties (eg headers). Before we use this function - // need to ensure it captures everything documented by Express and HTTP modules. - var wrapper = { - _req: req - }; - var toWrap = [ - "param", - "get", - "is", - "acceptsCharset", - "acceptsLanguage", - "app", - "baseUrl", - "body", - "cookies", - "fresh", - "hostname", - "ip", - "ips", - "originalUrl", - "params", - "path", - "protocol", - "query", - "route", - "secure", - "signedCookies", - "stale", - "subdomains", - "xhr", - "socket" // TODO: tidy this up - ]; - toWrap.forEach(function(f) { - if (typeof req[f] === "function") { - wrapper[f] = function() { - node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.req."+f})); - var result = req[f].apply(req,arguments); - if (result === req) { - return wrapper; - } else { - return result; - } - } - } else { - wrapper[f] = req[f]; - } - }); - - - return wrapper; - } - function createResponseWrapper(node,res) { - var wrapper = { - _res: res - }; - var toWrap = [ - "append", - "attachment", - "cookie", - "clearCookie", - "download", - "end", - "format", - "get", - "json", - "jsonp", - "links", - "location", - "redirect", - "render", - "send", - "sendfile", - "sendFile", - "sendStatus", - "set", - "status", - "type", - "vary" - ]; - toWrap.forEach(function(f) { - wrapper[f] = function() { - node.warn(RED._("httpin.errors.deprecated-call",{method:"msg.res."+f})); - var result = res[f].apply(res,arguments); - if (result === res) { - return wrapper; - } else { - return result; - } - } - }); - return wrapper; - } - - var corsHandler = function(req,res,next) { next(); } - - if (RED.settings.httpNodeCors) { - corsHandler = cors(RED.settings.httpNodeCors); - RED.httpNode.options("*",corsHandler); - } - - function HTTPIn(n) { - RED.nodes.createNode(this,n); - if (RED.settings.httpNodeRoot !== false) { - - if (!n.url) { - this.warn(RED._("httpin.errors.missing-path")); - return; - } - this.url = n.url; - if (this.url[0] !== '/') { - this.url = '/'+this.url; - } - this.method = n.method; - this.upload = n.upload; - this.swaggerDoc = n.swaggerDoc; - - var node = this; - - this.errorHandler = function(err,req,res,next) { - node.warn(err); - res.sendStatus(500); - }; - - this.callback = function(req,res) { - var msgid = RED.util.generateId(); - res._msgid = msgid; - if (node.method.match(/^(post|delete|put|options|patch)$/)) { - node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body}); - } else if (node.method == "get") { - node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.query}); - } else { - node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res)}); - } - }; - - var httpMiddleware = function(req,res,next) { next(); } - - if (RED.settings.httpNodeMiddleware) { - if (typeof RED.settings.httpNodeMiddleware === "function" || Array.isArray(RED.settings.httpNodeMiddleware)) { - httpMiddleware = RED.settings.httpNodeMiddleware; - } - } - - var maxApiRequestSize = RED.settings.apiMaxLength || '5mb'; - var jsonParser = bodyParser.json({limit:maxApiRequestSize}); - var urlencParser = bodyParser.urlencoded({limit:maxApiRequestSize,extended:true}); - - var metricsHandler = function(req,res,next) { next(); } - if (this.metric()) { - metricsHandler = function(req, res, next) { - var startAt = process.hrtime(); - onHeaders(res, function() { - if (res._msgid) { - var diff = process.hrtime(startAt); - var ms = diff[0] * 1e3 + diff[1] * 1e-6; - var metricResponseTime = ms.toFixed(3); - var metricContentLength = res.getHeader("content-length"); - //assuming that _id has been set for res._metrics in HttpOut node! - node.metric("response.time.millis", {_msgid:res._msgid} , metricResponseTime); - node.metric("response.content-length.bytes", {_msgid:res._msgid} , metricContentLength); - } - }); - next(); - }; - } - - var multipartParser = function(req,res,next) { next(); } - if (this.upload) { - var mp = multer({ storage: multer.memoryStorage() }).any(); - multipartParser = function(req,res,next) { - mp(req,res,function(err) { - req._body = true; - next(err); - }) - }; - } - - if (this.method == "get") { - RED.httpNode.get(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,this.callback,this.errorHandler); - } else if (this.method == "post") { - RED.httpNode.post(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,multipartParser,rawBodyParser,this.callback,this.errorHandler); - } else if (this.method == "put") { - RED.httpNode.put(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); - } else if (this.method == "patch") { - RED.httpNode.patch(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); - } else if (this.method == "delete") { - RED.httpNode.delete(this.url,cookieParser(),httpMiddleware,corsHandler,metricsHandler,jsonParser,urlencParser,rawBodyParser,this.callback,this.errorHandler); - } - - this.on("close",function() { - var node = this; - RED.httpNode._router.stack.forEach(function(route,i,routes) { - if (route.route && route.route.path === node.url && route.route.methods[node.method]) { - routes.splice(i,1); - } - }); - }); - } else { - this.warn(RED._("httpin.errors.not-created")); - } - } - RED.nodes.registerType("http in",HTTPIn); - - - function HTTPOut(n) { - RED.nodes.createNode(this,n); - var node = this; - this.headers = n.headers||{}; - this.statusCode = n.statusCode; - this.on("input",function(msg,_send,done) { - if (msg.res) { - var headers = RED.util.cloneMessage(node.headers); - if (msg.headers) { - if (msg.headers.hasOwnProperty('x-node-red-request-node')) { - var headerHash = msg.headers['x-node-red-request-node']; - delete msg.headers['x-node-red-request-node']; - var hash = hashSum(msg.headers); - if (hash === headerHash) { - delete msg.headers; - } - } - if (msg.headers) { - for (var h in msg.headers) { - if (msg.headers.hasOwnProperty(h) && !headers.hasOwnProperty(h)) { - headers[h] = msg.headers[h]; - } - } - } - } - if (Object.keys(headers).length > 0) { - msg.res._res.set(headers); - } - if (msg.cookies) { - for (var name in msg.cookies) { - if (msg.cookies.hasOwnProperty(name)) { - if (msg.cookies[name] === null || msg.cookies[name].value === null) { - if (msg.cookies[name]!==null) { - msg.res._res.clearCookie(name,msg.cookies[name]); - } else { - msg.res._res.clearCookie(name); - } - } else if (typeof msg.cookies[name] === 'object') { - msg.res._res.cookie(name,msg.cookies[name].value,msg.cookies[name]); - } else { - msg.res._res.cookie(name,msg.cookies[name]); - } - } - } - } - var statusCode = node.statusCode || msg.statusCode || 200; - if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) { - msg.res._res.status(statusCode).jsonp(msg.payload); - } else { - if (msg.res._res.get('content-length') == null) { - var len; - if (msg.payload == null) { - len = 0; - } else if (Buffer.isBuffer(msg.payload)) { - len = msg.payload.length; - } else if (typeof msg.payload == "number") { - len = Buffer.byteLength(""+msg.payload); - } else { - len = Buffer.byteLength(msg.payload); - } - msg.res._res.set('content-length', len); - } - - if (typeof msg.payload === "number") { - msg.payload = ""+msg.payload; - } - msg.res._res.status(statusCode).send(msg.payload); - } - } else { - node.warn(RED._("httpin.errors.no-response")); - } - done(); - }); - } - RED.nodes.registerType("http response",HTTPOut); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html deleted file mode 100644 index bf1f76b7f..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html b/packages/node_modules/@node-red/nodes/core/network/22-websocket.html deleted file mode 100644 index e3ee30eeb..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html deleted file mode 100644 index 39e987851..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js deleted file mode 100644 index 2f59227c8..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js +++ /dev/null @@ -1,719 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var reconnectTime = RED.settings.socketReconnectTime||10000; - var socketTimeout = RED.settings.socketTimeout||null; - const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000; - const Denque = require('denque'); - var net = require('net'); - - var connectionPool = {}; - - /** - * Enqueue `item` in `queue` - * @param {Denque} queue - Queue - * @param {*} item - Item to enqueue - * @private - * @returns {Denque} `queue` - */ - const enqueue = (queue, item) => { - // drop msgs from front of queue if size is going to be exceeded - if (queue.length === msgQueueSize) { queue.shift(); } - queue.push(item); - return queue; - }; - - /** - * Shifts item off front of queue - * @param {Deque} queue - Queue - * @private - * @returns {*} Item previously at front of queue - */ - const dequeue = queue => queue.shift(); - - function TcpIn(n) { - RED.nodes.createNode(this,n); - this.host = n.host; - this.port = n.port * 1; - this.topic = n.topic; - this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/ - this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */ - this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r"); - this.base64 = n.base64; - this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server"); - this.closing = false; - this.connected = false; - var node = this; - var count = 0; - - if (!node.server) { - var buffer = null; - var client; - var reconnectTimeout; - var end = false; - var setupTcpClient = function() { - node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); - node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); - var id = RED.util.generateId(); - client = net.connect(node.port, node.host, function() { - buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - node.connected = true; - node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); - }); - client.setKeepAlive(true,120000); - connectionPool[id] = client; - - client.on('data', function (data) { - if (node.datatype != 'buffer') { - data = data.toString(node.datatype); - } - if (node.stream) { - var msg; - if ((node.datatype) === "utf8" && node.newline !== "") { - buffer = buffer+data; - var parts = buffer.split(node.newline); - for (var i = 0; i 0)) { - var msg = {topic:node.topic, payload:buffer}; - msg._session = {type:"tcp",id:id}; - if (buffer.length !== 0) { - end = true; // only ask for fast re-connect if we actually got something - node.send(msg); - } - buffer = null; - } - }); - client.on('close', function() { - delete connectionPool[id]; - node.connected = false; - node.status({fill:"red",shape:"ring",text:"common.status.disconnected",_session:{type:"tcp",id:id}}); - if (!node.closing) { - if (end) { // if we were asked to close then try to reconnect once very quick. - end = false; - reconnectTimeout = setTimeout(setupTcpClient, 20); - } - else { - node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port})); - reconnectTimeout = setTimeout(setupTcpClient, reconnectTime); - } - } else { - if (node.doneClose) { node.doneClose(); } - } - }); - client.on('error', function(err) { - node.log(err); - }); - } - setupTcpClient(); - - this.on('close', function(done) { - node.doneClose = done; - this.closing = true; - if (client) { client.destroy(); } - clearTimeout(reconnectTimeout); - if (!node.connected) { done(); } - }); - } - else { - var server = net.createServer(function (socket) { - socket.setKeepAlive(true,120000); - if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } - var id = RED.util.generateId(); - var fromi; - var fromp; - connectionPool[id] = socket; - count++; - node.status({ - text:RED._("tcpin.status.connections",{count:count}), - event:"connect", - ip:socket.remoteAddress, - port:socket.remotePort, - _session: {type:"tcp",id:id} - }); - - var buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - socket.on('data', function (data) { - if (node.datatype != 'buffer') { - data = data.toString(node.datatype); - } - if (node.stream) { - var msg; - if ((typeof data) === "string" && node.newline !== "") { - buffer = buffer+data; - var parts = buffer.split(node.newline); - for (var i = 0; i 0) { - var msg = {topic:node.topic, payload:buffer, ip:fromi, port:fromp}; - msg._session = {type:"tcp",id:id}; - node.send(msg); - } - buffer = null; - } - }); - socket.on('timeout', function() { - node.log(RED._("tcpin.errors.timeout",{port:node.port})); - socket.end(); - }); - socket.on('close', function() { - delete connectionPool[id]; - count--; - node.status({ - text:RED._("tcpin.status.connections",{count:count}), - event:"disconnect", - ip:socket.remoteAddress, - port:socket.remotePort, - _session: {type:"tcp",id:id} - - }); - }); - socket.on('error',function(err) { - node.log(err); - }); - }); - - server.on('error', function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } - }); - - server.listen(node.port, function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } else { - node.log(RED._("tcpin.status.listening-port",{port:node.port})); - node.on('close', function() { - for (var c in connectionPool) { - if (connectionPool.hasOwnProperty(c)) { - connectionPool[c].end(); - connectionPool[c].unref(); - } - } - node.closing = true; - server.close(); - node.log(RED._("tcpin.status.stopped-listening",{port:node.port})); - }); - } - }); - } - } - RED.nodes.registerType("tcp in",TcpIn); - - - function TcpOut(n) { - RED.nodes.createNode(this,n); - this.host = n.host; - this.port = n.port * 1; - this.base64 = n.base64; - this.doend = n.end || false; - this.beserver = n.beserver; - this.name = n.name; - this.closing = false; - this.connected = false; - var node = this; - - if (!node.beserver||node.beserver=="client") { - var reconnectTimeout; - var client = null; - var end = false; - - var setupTcpClient = function() { - node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); - node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); - client = net.connect(node.port, node.host, function() { - node.connected = true; - node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - client.setKeepAlive(true,120000); - client.on('error', function (err) { - node.log(RED._("tcpin.errors.error",{error:err.toString()})); - }); - client.on('end', function (err) { - node.status({}); - node.connected = false; - }); - client.on('close', function() { - node.status({fill:"red",shape:"ring",text:"common.status.disconnected"}); - node.connected = false; - client.destroy(); - if (!node.closing) { - if (end) { - end = false; - reconnectTimeout = setTimeout(setupTcpClient,20); - } - else { - node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port})); - reconnectTimeout = setTimeout(setupTcpClient,reconnectTime); - } - } else { - if (node.doneClose) { node.doneClose(); } - } - }); - } - setupTcpClient(); - - node.on("input", function(msg, nodeSend, nodeDone) { - if (node.connected && msg.payload != null) { - if (Buffer.isBuffer(msg.payload)) { - client.write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - client.write(Buffer.from(msg.payload,'base64')); - } else { - client.write(Buffer.from(""+msg.payload)); - } - if (node.doend === true) { - end = true; - if (client) { node.status({}); client.destroy(); } - } - } - nodeDone(); - }); - - node.on("close", function(done) { - node.doneClose = done; - this.closing = true; - if (client) { client.destroy(); } - clearTimeout(reconnectTimeout); - if (!node.connected) { done(); } - }); - - } - else if (node.beserver == "reply") { - node.on("input",function(msg, nodeSend, nodeDone) { - if (msg._session && msg._session.type == "tcp") { - var client = connectionPool[msg._session.id]; - if (client) { - if (Buffer.isBuffer(msg.payload)) { - client.write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - client.write(Buffer.from(msg.payload,'base64')); - } else { - client.write(Buffer.from(""+msg.payload)); - } - } - } - else { - for (var i in connectionPool) { - if (Buffer.isBuffer(msg.payload)) { - connectionPool[i].write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - connectionPool[i].write(Buffer.from(msg.payload,'base64')); - } else { - connectionPool[i].write(Buffer.from(""+msg.payload)); - } - } - } - nodeDone(); - }); - } - else { - var connectedSockets = []; - node.status({text:RED._("tcpin.status.connections",{count:0})}); - var server = net.createServer(function (socket) { - socket.setKeepAlive(true,120000); - if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } - node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort})); - socket.on('timeout', function() { - node.log(RED._("tcpin.errors.timeout",{port:node.port})); - socket.end(); - }); - socket.on('data', function(d) { - // console.log("DATA",d) - }); - socket.on('close',function() { - node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort})); - connectedSockets.splice(connectedSockets.indexOf(socket),1); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - socket.on('error',function() { - node.log(RED._("tcpin.errors.socket-error",{host:socket.remoteAddress, port:socket.remotePort})); - connectedSockets.splice(connectedSockets.indexOf(socket),1); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - connectedSockets.push(socket); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - - node.on("input", function(msg, nodeSend, nodeDone) { - if (msg.payload != null) { - var buffer; - if (Buffer.isBuffer(msg.payload)) { - buffer = msg.payload; - } else if (typeof msg.payload === "string" && node.base64) { - buffer = Buffer.from(msg.payload,'base64'); - } else { - buffer = Buffer.from(""+msg.payload); - } - for (var i = 0; i < connectedSockets.length; i += 1) { - if (node.doend === true) { connectedSockets[i].end(buffer); } - else { connectedSockets[i].write(buffer); } - } - } - nodeDone(); - }); - - server.on('error', function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } - }); - - server.listen(node.port, function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } else { - node.log(RED._("tcpin.status.listening-port",{port:node.port})); - node.on('close', function() { - for (var c in connectedSockets) { - if (connectedSockets.hasOwnProperty(c)) { - connectedSockets[c].end(); - connectedSockets[c].unref(); - } - } - server.close(); - node.log(RED._("tcpin.status.stopped-listening",{port:node.port})); - }); - } - }); - } - } - RED.nodes.registerType("tcp out",TcpOut); - - - function TcpGet(n) { - RED.nodes.createNode(this,n); - this.server = n.server; - this.port = Number(n.port); - this.out = n.out; - this.ret = n.ret || "buffer"; - this.splitc = n.splitc; - - if (this.out === "immed") { this.splitc = -1; this.out = "time"; } - if (this.out !== "char") { this.splitc = Number(this.splitc); } - else { - if (this.splitc[0] == '\\') { - this.splitc = parseInt(this.splitc.replace("\\n",0x0A).replace("\\r",0x0D).replace("\\t",0x09).replace("\\e",0x1B).replace("\\f",0x0C).replace("\\0",0x00)); - } // jshint ignore:line - if (typeof this.splitc == "string") { - if (this.splitc.substr(0,2) == "0x") { - this.splitc = parseInt(this.splitc); - } - else { - this.splitc = this.splitc.charCodeAt(0); - } - } // jshint ignore:line - } - - var node = this; - - var clients = {}; - - this.on("input", function(msg, nodeSend, nodeDone) { - var i = 0; - if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) { - msg.payload = msg.payload.toString(); - } - - var host = node.server || msg.host; - var port = node.port || msg.port; - - // Store client information independently - // the clients object will have: - // clients[id].client, clients[id].msg, clients[id].timeout - var connection_id = host + ":" + port; - if (connection_id !== node.last_id) { - node.status({}); - node.last_id = connection_id; - } - clients[connection_id] = clients[connection_id] || { - msgQueue: new Denque(), - connected: false, - connecting: false - }; - enqueue(clients[connection_id].msgQueue, {msg:msg, nodeSend:nodeSend, nodeDone:nodeDone}); - clients[connection_id].lastMsg = msg; - - if (!clients[connection_id].connecting && !clients[connection_id].connected) { - var buf; - if (this.out == "count") { - if (this.splitc === 0) { buf = Buffer.alloc(1); } - else { buf = Buffer.alloc(this.splitc); } - } - else { buf = Buffer.alloc(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully - - clients[connection_id].client = net.Socket(); - if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);} - - if (host && port) { - clients[connection_id].connecting = true; - clients[connection_id].client.connect(port, host, function() { - //node.log(RED._("tcpin.errors.client-connected")); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].connected = true; - clients[connection_id].connecting = false; - let event; - while (event = dequeue(clients[connection_id].msgQueue)) { - clients[connection_id].client.write(event.msg.payload); - event.nodeDone(); - } - if (node.out === "time" && node.splitc < 0) { - clients[connection_id].connected = clients[connection_id].connecting = false; - clients[connection_id].client.end(); - delete clients[connection_id]; - node.status({}); - } - } - }); - } - else { - node.warn(RED._("tcpin.errors.no-host")); - } - - clients[connection_id].client.on('data', function(data) { - if (node.out === "sit") { // if we are staying connected just send the buffer - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = RED.util.cloneMessage(data); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - } - } - // else if (node.splitc === 0) { - // clients[connection_id].msg.payload = data; - // node.send(clients[connection_id].msg); - // } - else { - for (var j = 0; j < data.length; j++ ) { - if (node.out === "time") { - if (clients[connection_id]) { - // do the timer thing - if (clients[connection_id].timeout) { - i += 1; - buf[i] = data[j]; - } - else { - clients[connection_id].timeout = setTimeout(function () { - if (clients[connection_id]) { - clients[connection_id].timeout = null; - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i+1); - buf.copy(msg.payload,0,0,i+1); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - } - }, node.splitc); - i = 0; - buf[0] = data[j]; - } - } - } - // count bytes into a buffer... - else if (node.out == "count") { - buf[i] = data[j]; - i += 1; - if ( i >= node.splitc) { - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i); - buf.copy(msg.payload,0,0,i); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - i = 0; - } - } - } - // look for a char - else { - buf[i] = data[j]; - i += 1; - if (data[j] == node.splitc) { - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i); - buf.copy(msg.payload,0,0,i); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - i = 0; - } - } - } - } - } - }); - - clients[connection_id].client.on('end', function() { - //console.log("END"); - node.status({fill:"grey",shape:"ring",text:"common.status.disconnected"}); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].connected = clients[connection_id].connecting = false; - clients[connection_id].client = null; - } - }); - - clients[connection_id].client.on('close', function() { - //console.log("CLOSE"); - if (clients[connection_id]) { - clients[connection_id].connected = clients[connection_id].connecting = false; - } - - var anyConnected = false; - - for (var client in clients) { - if (clients[client].connected) { - anyConnected = true; - break; - } - } - if (node.doneClose && !anyConnected) { - clients = {}; - node.doneClose(); - } - }); - - clients[connection_id].client.on('error', function() { - //console.log("ERROR"); - node.status({fill:"red",shape:"ring",text:"common.status.error"}); - node.error(RED._("tcpin.errors.connect-fail") + " " + connection_id, msg); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - }); - - clients[connection_id].client.on('timeout',function() { - //console.log("TIMEOUT"); - if (clients[connection_id]) { - clients[connection_id].connected = clients[connection_id].connecting = false; - node.status({fill:"grey",shape:"dot",text:"tcpin.errors.connect-timeout"}); - //node.warn(RED._("tcpin.errors.connect-timeout")); - if (clients[connection_id].client) { - clients[connection_id].connecting = true; - clients[connection_id].client.connect(port, host, function() { - clients[connection_id].connected = true; - clients[connection_id].connecting = false; - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - } - } - }); - } - else if (!clients[connection_id].connecting && clients[connection_id].connected) { - if (clients[connection_id] && clients[connection_id].client) { - let event = dequeue(clients[connection_id].msgQueue) - clients[connection_id].client.write(event.msg.payload); - event.nodeDone(); - } - } - }); - - this.on("close", function(done) { - node.doneClose = done; - for (var cl in clients) { - if (clients[cl].hasOwnProperty("client")) { - clients[cl].client.destroy(); - } - } - node.status({}); - - // this is probably not necessary and may be removed - var anyConnected = false; - for (var c in clients) { - if (clients[c].connected) { - anyConnected = true; - break; - } - } - if (!anyConnected) { clients = {}; } - done(); - }); - - } - RED.nodes.registerType("tcp request",TcpGet); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/32-udp.html b/packages/node_modules/@node-red/nodes/core/network/32-udp.html deleted file mode 100644 index 3a4e241af..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/32-udp.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/32-udp.js b/packages/node_modules/@node-red/nodes/core/network/32-udp.js deleted file mode 100644 index d42d3a7c3..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/32-udp.js +++ /dev/null @@ -1,280 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var os = require('os'); - var dgram = require('dgram'); - var udpInputPortsInUse = {}; - - // The Input Node - function UDPin(n) { - RED.nodes.createNode(this,n); - this.group = n.group; - this.port = n.port; - this.datatype = n.datatype; - this.iface = n.iface || null; - this.multicast = n.multicast; - this.ipv = n.ipv || "udp4"; - var node = this; - - if (node.iface && node.iface.indexOf(".") === -1) { - try { - if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } - } - else { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } - } - } - catch(e) { - node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface})); - node.iface = null; - } - } - - var opts = {type:node.ipv, reuseAddr:true}; - if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; } - var server; - - if (!udpInputPortsInUse.hasOwnProperty(node.port)) { - server = dgram.createSocket(opts); // default to udp4 - server.bind(node.port, function() { - if (node.multicast == "true") { - server.setBroadcast(true); - server.setMulticastLoopback(false); - try { - server.setMulticastTTL(128); - server.addMembership(node.group,node.iface); - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - node.log(RED._("udp.status.mc-group",{group:node.group})); - } catch (e) { - if (e.errno == "EINVAL") { - node.error(RED._("udp.errors.bad-mcaddress")); - } else if (e.errno == "ENODEV") { - node.error(RED._("udp.errors.interface")); - } else { - node.error(RED._("udp.errors.error",{error:e.errno})); - } - } - } - }); - udpInputPortsInUse[node.port] = server; - } - else { - node.log(RED._("udp.errors.alreadyused",{port:node.port})); - server = udpInputPortsInUse[node.port]; // re-use existing - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - } - - server.on("error", function (err) { - if ((err.code == "EACCES") && (node.port < 1024)) { - node.error(RED._("udp.errors.access-error")); - } else { - node.error(RED._("udp.errors.error",{error:err.code})); - } - server.close(); - }); - - server.on('message', function (message, remote) { - var msg; - if (node.datatype =="base64") { - msg = { payload:message.toString('base64'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; - } else if (node.datatype =="utf8") { - msg = { payload:message.toString('utf8'), fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; - } else { - msg = { payload:message, fromip:remote.address+':'+remote.port, ip:remote.address, port:remote.port }; - } - node.send(msg); - }); - - server.on('listening', function () { - var address = server.address(); - node.log(RED._("udp.status.listener-at",{host:node.iface||address.address,port:address.port})); - - }); - - node.on("close", function() { - try { - if (node.multicast == "true") { server.dropMembership(node.group); } - server.close(); - node.log(RED._("udp.status.listener-stopped")); - } catch (err) { - //node.error(err); - } - if (udpInputPortsInUse.hasOwnProperty(node.port)) { - delete udpInputPortsInUse[node.port]; - } - node.status({}); - }); - - } - RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-ports.read'), function(req,res) { - res.json(Object.keys(udpInputPortsInUse)); - }); - RED.nodes.registerType("udp in",UDPin); - - - - // The Output Node - function UDPout(n) { - RED.nodes.createNode(this,n); - //this.group = n.group; - this.port = n.port; - this.outport = n.outport||""; - this.base64 = n.base64; - this.addr = n.addr; - this.iface = n.iface || null; - this.multicast = n.multicast; - this.ipv = n.ipv || "udp4"; - var node = this; - - if (node.iface && node.iface.indexOf(".") === -1) { - try { - if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } - } - else { - if (node.ipv === "udp4") { - node.iface = (os.networkInterfaces())[node.iface][0].address; - } else { - node.iface = (os.networkInterfaces())[node.iface][1].address; - } - } - } - catch(e) { - node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface})); - node.iface = null; - } - } - - var opts = {type:node.ipv, reuseAddr:true}; - - var sock; - var p = this.outport || this.port || "0"; - node.tout = setTimeout(function() { - if ((p != 0) && udpInputPortsInUse[p]) { - sock = udpInputPortsInUse[p]; - if (node.multicast != "false") { - sock.setBroadcast(true); - sock.setMulticastLoopback(false); - } - node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port})); - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - } - else { - sock = dgram.createSocket(opts); // default to udp4 - if (node.multicast != "false") { - sock.bind(node.outport, function() { // have to bind before you can enable broadcast... - sock.setBroadcast(true); // turn on broadcast - sock.setMulticastLoopback(false); // turn off loopback - if (node.multicast == "multi") { - try { - sock.setMulticastTTL(128); - sock.addMembership(node.addr,node.iface); // Add to the multicast group - if (node.iface) { node.status({text:n.iface+" : "+node.iface}); } - node.log(RED._("udp.status.mc-ready",{iface:node.iface,outport:node.outport,host:node.addr,port:node.port})); - } catch (e) { - if (e.errno == "EINVAL") { - node.error(RED._("udp.errors.bad-mcaddress")); - } else if (e.errno == "ENODEV") { - node.error(RED._("udp.errors.interface")); - } else { - node.error(RED._("udp.errors.error",{error:e.errno})); - } - } - } else { - node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port})); - } - }); - } else if ((node.outport !== "") && (!udpInputPortsInUse[node.outport])) { - sock.bind(node.outport); - node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port})); - } else { - node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port})); - } - sock.on("error", function(err) { - // Any async error will also get reported in the sock.send call. - // This handler is needed to ensure the error marked as handled to - // prevent it going to the global error handler and shutting node-red - // down. - }); - udpInputPortsInUse[p] = sock; - } - - node.on("input", function(msg, nodeSend, nodeDone) { - if (msg.hasOwnProperty("payload")) { - var add = node.addr || msg.ip || ""; - var por = node.port || msg.port || 0; - if (add === "") { - node.warn(RED._("udp.errors.ip-notset")); - nodeDone(); - } else if (por === 0) { - node.warn(RED._("udp.errors.port-notset")); - nodeDone(); - } else if (isNaN(por) || (por < 1) || (por > 65535)) { - node.warn(RED._("udp.errors.port-invalid")); - nodeDone(); - } else { - var message; - if (node.base64) { - message = Buffer.from(msg.payload, 'base64'); - } else if (msg.payload instanceof Buffer) { - message = msg.payload; - } else { - message = Buffer.from(""+msg.payload); - } - sock.send(message, 0, message.length, por, add, function(err, bytes) { - if (err) { - node.error("udp : "+err,msg); - } - message = null; - nodeDone(); - }); - } - } - }); - }, 75); - - node.on("close", function() { - if (node.tout) { clearTimeout(node.tout); } - try { - if (node.multicast == "multi") { sock.dropMembership(node.group); } - sock.close(); - node.log(RED._("udp.status.output-stopped")); - } catch (err) { - //node.error(err); - } - if (udpInputPortsInUse.hasOwnProperty(p)) { - delete udpInputPortsInUse[p]; - } - node.status({}); - }); - } - RED.nodes.registerType("udp out",UDPout); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js b/packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js deleted file mode 100644 index c472f01e9..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/lib/mqtt.js +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var util = require("util"); -var mqtt = require("mqtt"); -var events = require("events"); - -util.log("[warn] nodes/core/io/lib/mqtt.js is deprecated and will be removed in a future release of Node-RED. Please report this usage to the Node-RED mailing list."); - -//var inspect = require("util").inspect; - -//var Client = module.exports.Client = function( - -var port = 1883; -var host = "localhost"; - -function MQTTClient(port,host) { - this.port = port||1883; - this.host = host||"localhost"; - this.messageId = 1; - this.pendingSubscriptions = {}; - this.inboundMessages = {}; - this.lastOutbound = (new Date()).getTime(); - this.lastInbound = (new Date()).getTime(); - this.connected = false; - - this._nextMessageId = function() { - this.messageId += 1; - if (this.messageId > 0xFFFF) { - this.messageId = 1; - } - return this.messageId; - } - events.EventEmitter.call(this); -} -util.inherits(MQTTClient, events.EventEmitter); - -MQTTClient.prototype.connect = function(options) { - if (!this.connected) { - var self = this; - options = options||{}; - self.options = options; - self.options.keepalive = options.keepalive||15; - self.options.clean = self.options.clean||true; - self.options.protocolId = 'MQIsdp'; - self.options.protocolVersion = 3; - - self.client = mqtt.createConnection(this.port,this.host,function(err,client) { - if (err) { - self.connected = false; - clearInterval(self.watchdog); - self.connectionError = true; - //util.log('[mqtt] ['+self.uid+'] connection error 1 : '+inspect(err)); - self.emit('connectionlost',err); - return; - } - client.on('close',function(e) { - //util.log('[mqtt] ['+self.uid+'] on close'); - clearInterval(self.watchdog); - if (!self.connectionError) { - if (self.connected) { - self.connected = false; - self.emit('connectionlost',e); - } else { - self.emit('disconnect'); - } - } - }); - client.on('error',function(e) { - //util.log('[mqtt] ['+self.uid+'] on error : '+inspect(e)); - clearInterval(self.watchdog); - if (self.connected) { - self.connected = false; - self.emit('connectionlost',e); - } - }); - client.on('connack',function(packet) { - if (packet.returnCode === 0) { - self.watchdog = setInterval(function(self) { - var now = (new Date()).getTime(); - - //util.log('[mqtt] ['+self.uid+'] watchdog '+inspect({connected:self.connected,connectionError:self.connectionError,pingOutstanding:self.pingOutstanding,now:now,lastOutbound:self.lastOutbound,lastInbound:self.lastInbound})); - - if (now - self.lastOutbound > self.options.keepalive*500 || now - self.lastInbound > self.options.keepalive*500) { - if (self.pingOutstanding) { - //util.log('[mqtt] ['+self.uid+'] watchdog pingOustanding - disconnect'); - try { - self.client.disconnect(); - } catch (err) { - } - } else { - //util.log('[mqtt] ['+self.uid+'] watchdog pinging'); - self.lastOutbound = (new Date()).getTime(); - self.lastInbound = (new Date()).getTime(); - self.pingOutstanding = true; - self.client.pingreq(); - } - } - - },self.options.keepalive*500,self); - self.pingOutstanding = false; - self.lastInbound = (new Date()).getTime() - self.lastOutbound = (new Date()).getTime() - self.connected = true; - self.connectionError = false; - self.emit('connect'); - } else { - self.connected = false; - self.emit('connectionlost'); - } - }); - client.on('suback',function(packet) { - self.lastInbound = (new Date()).getTime() - var topic = self.pendingSubscriptions[packet.messageId]; - self.emit('subscribe',topic,packet.granted[0]); - delete self.pendingSubscriptions[packet.messageId]; - }); - client.on('unsuback',function(packet) { - self.lastInbound = (new Date()).getTime() - var topic = self.pendingSubscriptions[packet.messageId]; - self.emit('unsubscribe',topic); - delete self.pendingSubscriptions[packet.messageId]; - }); - client.on('publish',function(packet) { - self.lastInbound = (new Date()).getTime(); - if (packet.qos < 2) { - var p = packet; - self.emit('message',p.topic,p.payload,p.qos,p.retain); - } else { - self.inboundMessages[packet.messageId] = packet; - this.lastOutbound = (new Date()).getTime() - self.client.pubrec(packet); - } - if (packet.qos == 1) { - this.lastOutbound = (new Date()).getTime() - self.client.puback(packet); - } - }); - - client.on('pubrel',function(packet) { - self.lastInbound = (new Date()).getTime() - var p = self.inboundMessages[packet.messageId]; - if (p) { - self.emit('message',p.topic,p.payload,p.qos,p.retain); - delete self.inboundMessages[packet.messageId]; - } - self.lastOutbound = (new Date()).getTime() - self.client.pubcomp(packet); - }); - - client.on('puback',function(packet) { - self.lastInbound = (new Date()).getTime() - // outbound qos-1 complete - }); - - client.on('pubrec',function(packet) { - self.lastInbound = (new Date()).getTime() - self.lastOutbound = (new Date()).getTime() - self.client.pubrel(packet); - }); - client.on('pubcomp',function(packet) { - self.lastInbound = (new Date()).getTime() - // outbound qos-2 complete - }); - client.on('pingresp',function(packet) { - //util.log('[mqtt] ['+self.uid+'] received pingresp'); - self.lastInbound = (new Date()).getTime() - self.pingOutstanding = false; - }); - - this.lastOutbound = (new Date()).getTime() - this.connectionError = false; - client.connect(self.options); - }); - } -} - -MQTTClient.prototype.subscribe = function(topic,qos) { - var self = this; - if (self.connected) { - var options = { - subscriptions:[{topic:topic,qos:qos}], - messageId: self._nextMessageId() - }; - this.pendingSubscriptions[options.messageId] = topic; - this.lastOutbound = (new Date()).getTime(); - self.client.subscribe(options); - self.client.setPacketEncoding('binary'); - } -} -MQTTClient.prototype.unsubscribe = function(topic) { - var self = this; - if (self.connected) { - var options = { - unsubscriptions:[topic], - messageId: self._nextMessageId() - }; - this.pendingSubscriptions[options.messageId] = topic; - this.lastOutbound = (new Date()).getTime() - self.client.unsubscribe(options); - } -} - -MQTTClient.prototype.publish = function(topic,payload,qos,retain) { - var self = this; - if (self.connected) { - - if (!Buffer.isBuffer(payload)) { - if (typeof payload === "object") { - payload = JSON.stringify(payload); - } else if (typeof payload !== "string") { - payload = ""+payload; - } - } - var options = { - topic: topic, - payload: payload, - qos: qos||0, - retain:retain||false - }; - if (options.qos !== 0) { - options.messageId = self._nextMessageId(); - } - this.lastOutbound = (new Date()).getTime() - self.client.publish(options); - } -} - -MQTTClient.prototype.disconnect = function() { - var self = this; - if (this.connected) { - this.connected = false; - try { - this.client.disconnect(); - } catch(err) { - } - } -} -MQTTClient.prototype.isConnected = function() { - return this.connected; -} -module.exports.createClient = function(port,host) { - var mqtt_client = new MQTTClient(port,host); - return mqtt_client; -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html deleted file mode 100644 index 80778ac1b..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js deleted file mode 100644 index 184f40bdd..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - function CSVNode(n) { - RED.nodes.createNode(this,n); - this.template = (n.temp || ""); - this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r"); - this.quo = '"'; - this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r"); - this.winflag = (this.ret === "\r\n"); - this.lineend = "\n"; - this.multi = n.multi || "one"; - this.hdrin = n.hdrin || false; - this.hdrout = n.hdrout || "none"; - this.goodtmpl = true; - this.skip = parseInt(n.skip || 0); - this.store = []; - this.parsestrings = n.strings; - this.include_empty_strings = n.include_empty_strings || false; - this.include_null_values = n.include_null_values || false; - if (this.parsestrings === undefined) { this.parsestrings = true; } - if (this.hdrout === false) { this.hdrout = "none"; } - if (this.hdrout === true) { this.hdrout = "all"; } - var tmpwarn = true; - var node = this; - var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); - - // pass in an array of column names to be trimmed, de-quoted and retrimmed - var clean = function(col,sep) { - if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); } - col = col.trim().split(re) || [""]; - col = col.map(x => x.replace(/"/g,'').trim()); - if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; } - else { node.goodtmpl = true; } - return col; - } - var template = clean(node.template,','); - var notemplate = template.length === 1 && template[0] === ''; - node.hdrSent = false; - - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - node.hdrSent = false; - } - if (msg.hasOwnProperty("payload")) { - if (typeof msg.payload == "object") { // convert object to CSV string - try { - if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) { - template = clean(node.template); - } - var ou = ""; - if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; } - if (node.hdrout !== "none" && node.hdrSent === false) { - if ((template.length === 1) && (template[0] === '')) { - if (msg.hasOwnProperty("columns")) { - template = clean(msg.columns || "",","); - } - else { - template = Object.keys(msg.payload[0]); - } - } - ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret; - if (node.hdrout === "once") { node.hdrSent = true; } - } - for (var s = 0; s < msg.payload.length; s++) { - if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) { - if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; } - for (var t = 0; t < msg.payload[s].length; t++) { - if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; } - if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes - msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""'); - msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo; - } - else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas" - msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo; - } - } - ou += msg.payload[s].join(node.sep) + node.ret; - } - else { - if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) { - template = clean(msg.columns || "",","); - } - if ((template.length === 1) && (template[0] === '')) { - /* istanbul ignore else */ - if (tmpwarn === true) { // just warn about missing template once - node.warn(RED._("csv.errors.obj_csv")); - tmpwarn = false; - } - for (var p in msg.payload[0]) { - /* istanbul ignore else */ - if (msg.payload[s].hasOwnProperty(p)) { - /* istanbul ignore else */ - if (typeof msg.payload[s][p] !== "object") { - var q = "" + msg.payload[s][p]; - if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes - q = q.replace(/"/g, '""'); - ou += node.quo + q + node.quo + node.sep; - } - else if (q.indexOf(node.sep) !== -1) { // add quotes if any "commas" - ou += node.quo + q + node.quo + node.sep; - } - else { ou += q + node.sep; } // otherwise just add - } - } - } - ou = ou.slice(0,-1) + node.ret; - } - else { - for (var t=0; t < template.length; t++) { - if (template[t] === '') { - ou += node.sep; - } - else { - var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']")); - /* istanbul ignore else */ - if (p === "undefined") { p = ""; } - if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes - p = p.replace(/"/g, '""'); - ou += node.quo + p + node.quo + node.sep; - } - else if (p.indexOf(node.sep) !== -1) { // add quotes if any "commas" - ou += node.quo + p + node.quo + node.sep; - } - else { ou += p + node.sep; } // otherwise just add - } - } - ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline" - } - } - } - msg.payload = ou; - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(','); - if (msg.payload !== '') { send(msg); } - done(); - } - catch(e) { done(e); } - } - else if (typeof msg.payload == "string") { // convert CSV string to object - try { - var f = true; // flag to indicate if inside or outside a pair of quotes true = outside. - var j = 0; // pointer into array of template items - var k = [""]; // array of data for each of the template items - var o = {}; // output object to build up - var a = []; // output array is needed for multiline option - var first = true; // is this the first line - var last = false; - var line = msg.payload; - var linecount = 0; - var tmp = ""; - var has_parts = msg.hasOwnProperty("parts"); - var reg = /^[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+$/i; - if (msg.hasOwnProperty("parts")) { - linecount = msg.parts.index; - if (msg.parts.index > node.skip) { first = false; } - if (msg.parts.hasOwnProperty("count") && (msg.parts.index+1 >= msg.parts.count)) { last = true; } - } - - // For now we are just going to assume that any \r or \n means an end of line... - // got to be a weird csv that has singleton \r \n in it for another reason... - - // Now process the whole file/line - var nocr = (line.match(/[\r\n]/g)||[]).length; - if (has_parts && node.multi === "mult" && nocr > 1) { tmp = ""; first = true; } - for (var i = 0; i < line.length; i++) { - if (first && (linecount < node.skip)) { - if (line[i] === "\n") { linecount += 1; } - continue; - } - if ((node.hdrin === true) && first) { // if the template is in the first line - if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break - if (line.length - i === 1) { tmp += line[i]; } - template = clean(tmp,node.sep); - first = false; - } - else { tmp += line[i]; } - } - else { - if (line[i] === node.quo) { // if it's a quote toggle inside or outside - f = !f; - if (line[i-1] === node.quo) { - if (f === false) { k[j] += '\"'; } - } // if it's a quotequote then it's actually a quote - //if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; } - } - else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish - if (!node.goodtmpl) { template[j] = "col"+(j+1); } - if ( template[j] && (template[j] !== "") ) { - // if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null - if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null; - if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); } - if (node.include_null_values && k[j] === null) o[template[j]] = k[j]; - if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j]; - if (k[j] !== null && k[j] !== "") o[template[j]] = k[j]; - } - j += 1; - // if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",' - k[j] = line.length - 1 === i ? null : ""; - } - else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines - //console.log(j,k,o,k[j]); - if (!node.goodtmpl) { template[j] = "col"+(j+1); } - if ( template[j] && (template[j] !== "") ) { - // if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3' - if (line[i-1] === node.sep) k[j] = null; - if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); } - else { if (k[j] !== null) k[j].replace(/\r$/,''); } - if (node.include_null_values && k[j] === null) o[template[j]] = k[j]; - if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j]; - if (k[j] !== null && k[j] !== "") o[template[j]] = k[j]; - } - if (JSON.stringify(o) !== "{}") { // don't send empty objects - a.push(o); // add to the array - } - j = 0; - k = [""]; - o = {}; - f = true; // reset in/out flag ready for next line. - } - else { // just add to the part of the message - k[j] += line[i]; - } - } - } - // Finished so finalize and send anything left - if (f === false) { node.warn(RED._("csv.errors.bad_csv")); } - if (!node.goodtmpl) { template[j] = "col"+(j+1); } - - if ( template[j] && (template[j] !== "") ) { - if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); } - else { if (k[j] !== null) k[j].replace(/\r$/,''); } - if (node.include_null_values && k[j] === null) o[template[j]] = k[j]; - if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j]; - if (k[j] !== null && k[j] !== "") o[template[j]] = k[j]; - } - - if (JSON.stringify(o) !== "{}") { // don't send empty objects - a.push(o); // add to the array - } - - if (node.multi !== "one") { - msg.payload = a; - if (has_parts && nocr <= 1) { - if (JSON.stringify(o) !== "{}") { - node.store.push(o); - } - if (msg.parts.index + 1 === msg.parts.count) { - msg.payload = node.store; - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); - delete msg.parts; - send(msg); - node.store = []; - } - } - else { - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); - send(msg); // finally send the array - } - } - else { - var len = a.length; - for (var i = 0; i < len; i++) { - var newMessage = RED.util.cloneMessage(msg); - newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); - newMessage.payload = a[i]; - if (!has_parts) { - newMessage.parts = { - id: msg._msgid, - index: i, - count: len - }; - } - else { - newMessage.parts.index -= node.skip; - newMessage.parts.count -= node.skip; - if (node.hdrin) { // if we removed the header line then shift the counts by 1 - newMessage.parts.index -= 1; - newMessage.parts.count -= 1; - } - } - if (last) { newMessage.complete = true; } - send(newMessage); - } - if (has_parts && last && len === 0) { - send({complete:true}); - } - } - node.linecount = 0; - done(); - } - catch(e) { done(e); } - } - else { node.warn(RED._("csv.errors.csv_js")); done(); } - } - else { - if (!msg.hasOwnProperty("reset")) { - node.send(msg); // If no payload and not reset - just pass it on. - } - done(); - } - }); - } - RED.nodes.registerType("csv",CSVNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html b/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html deleted file mode 100644 index 4509dd054..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js deleted file mode 100644 index 073b98689..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-HTML.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var cheerio = require('cheerio'); - - function CheerioNode(n) { - RED.nodes.createNode(this,n); - this.property = n.property||"payload"; - this.outproperty = n.outproperty||this.property||"payload"; - this.tag = n.tag; - this.ret = n.ret || "html"; - this.as = n.as || "single"; - var node = this; - this.on("input", function(msg,send,done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var tag = node.tag; - if (msg.hasOwnProperty("select")) { tag = node.tag || msg.select; } - try { - var $ = cheerio.load(value); - var pay = []; - var count = 0; - $(tag).each(function() { - count++; - }); - var index = 0; - $(tag).each(function() { - if (node.as === "multi") { - var pay2 = null; - if (node.ret === "html") { pay2 = cheerio.load($(this).html().trim(),null,false).xml(); } - if (node.ret === "text") { pay2 = $(this).text(); } - if (node.ret === "attr") { - pay2 = Object.assign({},this.attribs); - } - //if (node.ret === "val") { pay2 = $(this).val(); } - /* istanbul ignore else */ - if (pay2) { - var new_msg = RED.util.cloneMessage(msg); - RED.util.setMessageProperty(new_msg,node.outproperty,pay2); - new_msg.parts = { - id: msg._msgid, - index: index, - count: count, - type: "string", - ch: "" - }; - send(new_msg); - } - } - if (node.as === "single") { - if (node.ret === "html") { pay.push( cheerio.load($(this).html().trim(),null,false).xml() ); } - if (node.ret === "text") { pay.push( $(this).text() ); } - if (node.ret === "attr") { - var attribs = Object.assign({},this.attribs); - pay.push( attribs ); - } - //if (node.ret === "val") { pay.push( $(this).val() ); } - } - index++; - }); - if (node.as === "single") { // Always return an array - even if blank - RED.util.setMessageProperty(msg,node.outproperty,pay); - send(msg); - } - done(); - } - catch (error) { - done(error.message); - } - } - else { send(msg); done(); } // If no payload - just pass it on. - }); - } - RED.nodes.registerType("html",CheerioNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html b/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html deleted file mode 100644 index 6599cf0e0..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js b/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js deleted file mode 100644 index a68edc681..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - const Ajv = require('ajv'); - const ajv = new Ajv({allErrors: true}); - ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); - - function JSONNode(n) { - RED.nodes.createNode(this,n); - this.indent = n.pretty ? 4 : 0; - this.action = n.action||""; - this.property = n.property||"payload"; - this.schema = null; - this.compiledSchema = null; - - var node = this; - - this.on("input", function(msg,send,done) { - var validate = false; - if (msg.schema) { - // If input schema is different, re-compile it - if (JSON.stringify(this.schema) != JSON.stringify(msg.schema)) { - try { - this.compiledSchema = ajv.compile(msg.schema); - this.schema = msg.schema; - } catch(e) { - this.schema = null; - this.compiledSchema = null; - done(RED._("json.errors.schema-error-compile")); - return; - } - } - validate = true; - } - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - if (typeof value === "string") { - if (node.action === "" || node.action === "obj") { - try { - RED.util.setMessageProperty(msg,node.property,JSON.parse(value)); - if (validate) { - if (this.compiledSchema(msg[node.property])) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - catch(e) { done(e.message); } - } else { - // If node.action is str and value is str - if (validate) { - if (this.compiledSchema(JSON.parse(msg[node.property]))) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - } - else if ((typeof value === "object") || (typeof value === "boolean") || (typeof value === "number")) { - if (node.action === "" || node.action === "str") { - if (!Buffer.isBuffer(value)) { - try { - if (validate) { - if (this.compiledSchema(value)) { - RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); - send(msg); - done(); - } - } - catch(e) { done(RED._("json.errors.dropped-error")); } - } - else { node.warn(RED._("json.errors.dropped-object")); done(); } - } else { - // If node.action is obj and value is object - if (validate) { - if (this.compiledSchema(value)) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - } - else { node.warn(RED._("json.errors.dropped")); done(); } - } - else { send(msg); done(); } // If no property - just pass it on. - }); - } - RED.nodes.registerType("json",JSONNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.html b/packages/node_modules/@node-red/nodes/core/parsers/70-XML.html deleted file mode 100644 index 642fe4d04..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js deleted file mode 100644 index a778c4d72..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js +++ /dev/null @@ -1,49 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - var xml2js = require('xml2js'); - var parseString = xml2js.parseString; - - function XMLNode(n) { - RED.nodes.createNode(this,n); - this.attrkey = n.attr; - this.charkey = n.chr; - this.property = n.property||"payload"; - var node = this; - this.on("input", function(msg,send,done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var options; - if (typeof value === "object") { - options = {renderOpts:{pretty:false}}; - if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; } - options.async = false; - var builder = new xml2js.Builder(options); - value = builder.buildObject(value, options); - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - else if (typeof value == "string") { - options = {}; - if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; } - options.async = true; - options.attrkey = node.attrkey || options.attrkey || '$'; - options.charkey = node.charkey || options.charkey || '_'; - parseString(value, options, function (err, result) { - if (err) { done(err); } - else { - value = result; - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - }); - } - else { node.warn(RED._("xml.errors.xml_js")); done(); } - } - else { send(msg); done(); } // If no property - just pass it on. - }); - } - RED.nodes.registerType("xml",XMLNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html b/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html deleted file mode 100644 index deaf47d95..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js deleted file mode 100644 index 69a6bf444..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-YAML.js +++ /dev/null @@ -1,41 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - var yaml = require('js-yaml'); - function YAMLNode(n) { - RED.nodes.createNode(this,n); - this.property = n.property||"payload"; - var node = this; - this.on("input", function(msg,send,done) { - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - if (typeof value === "string") { - try { - value = yaml.load(value); - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - catch(e) { done(e.message); } - } - else if (typeof value === "object") { - if (!Buffer.isBuffer(value)) { - try { - value = yaml.dump(value); - RED.util.setMessageProperty(msg,node.property,value); - send(msg); - done(); - } - catch(e) { - done(RED._("yaml.errors.dropped-error")); - } - } - else { node.warn(RED._("yaml.errors.dropped-object")); done(); } - } - else { node.warn(RED._("yaml.errors.dropped")); done(); } - } - else { send(msg); done(); } // If no payload - just pass it on. - }); - } - RED.nodes.registerType("yaml",YAMLNode); -}; diff --git a/packages/node_modules/@node-red/nodes/core/sequence/17-split.html b/packages/node_modules/@node-red/nodes/core/sequence/17-split.html deleted file mode 100644 index 265719d1e..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/17-split.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js b/packages/node_modules/@node-red/nodes/core/sequence/17-split.js deleted file mode 100644 index e158f344c..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js +++ /dev/null @@ -1,785 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - function sendArray(node,msg,array,send) { - for (var i = 0; i < array.length-1; i++) { - msg.payload = array[i]; - msg.parts.index = node.c++; - if (node.stream !== true) { msg.parts.count = array.length; } - send(RED.util.cloneMessage(msg)); - } - if (node.stream !== true) { - msg.payload = array[i]; - msg.parts.index = node.c++; - msg.parts.count = array.length; - send(RED.util.cloneMessage(msg)); - node.c = 0; - } - else { node.remainder = array[i]; } - } - - function SplitNode(n) { - RED.nodes.createNode(this,n); - var node = this; - node.stream = n.stream; - node.spltType = n.spltType || "str"; - node.addname = n.addname || ""; - try { - if (node.spltType === "str") { - this.splt = (n.splt || "\\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0"); - } else if (node.spltType === "bin") { - var spltArray = JSON.parse(n.splt); - if (Array.isArray(spltArray)) { - this.splt = Buffer.from(spltArray); - } else { - throw new Error("not an array"); - } - this.spltBuffer = spltArray; - } else if (node.spltType === "len") { - this.splt = parseInt(n.splt); - if (isNaN(this.splt) || this.splt < 1) { - throw new Error("invalid split length: "+n.splt); - } - } - this.arraySplt = (n.arraySplt === undefined)?1:parseInt(n.arraySplt); - if (isNaN(this.arraySplt) || this.arraySplt < 1) { - throw new Error("invalid array split length: "+n.arraySplt); - } - } catch(err) { - this.error("Invalid split property: "+err.toString()); - return; - } - node.c = 0; - node.buffer = Buffer.from([]); - node.pendingDones = []; - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("payload")) { - if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack - else { msg.parts = {}; } - msg.parts.id = RED.util.generateId(); // generate a random id - delete msg._msgid; - if (typeof msg.payload === "string") { // Split String into array - msg.payload = (node.remainder || "") + msg.payload; - msg.parts.type = "string"; - if (node.spltType === "len") { - msg.parts.ch = ""; - msg.parts.len = node.splt; - var count = msg.payload.length/node.splt; - if (Math.floor(count) !== count) { - count = Math.ceil(count); - } - if (node.stream !== true) { - msg.parts.count = count; - node.c = 0; - } - var pos = 0; - var data = msg.payload; - for (var i=0; i 1) { - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - node.remainder = data.substring(pos); - if ((node.stream !== true) || (node.remainder.length === node.splt)) { - msg.payload = node.remainder; - msg.parts.index = node.c++; - send(RED.util.cloneMessage(msg)); - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - done(); - node.remainder = ""; - } else { - node.pendingDones.push(done); - } - } - else { - var a = []; - if (node.spltType === "bin") { - if (!node.spltBufferString) { - node.spltBufferString = node.splt.toString(); - } - a = msg.payload.split(node.spltBufferString); - msg.parts.ch = node.spltBuffer; // pass the split char to other end for rejoin - } else if (node.spltType === "str") { - a = msg.payload.split(node.splt); - msg.parts.ch = node.splt; // pass the split char to other end for rejoin - } - sendArray(node,msg,a,send); - done(); - } - } - else if (Array.isArray(msg.payload)) { // then split array into messages - msg.parts.type = "array"; - var count = msg.payload.length/node.arraySplt; - if (Math.floor(count) !== count) { - count = Math.ceil(count); - } - msg.parts.count = count; - var pos = 0; - var data = msg.payload; - msg.parts.len = node.arraySplt; - for (var i=0; i 1) { - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - node.buffer = buff.slice(pos); - if ((node.stream !== true) || (node.buffer.length === node.splt)) { - msg.payload = node.buffer; - msg.parts.index = node.c++; - send(RED.util.cloneMessage(msg)); - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - done(); - node.buffer = Buffer.from([]); - } else { - node.pendingDones.push(done); - } - } - else { - var count = 0; - if (node.spltType === "bin") { - msg.parts.ch = node.spltBuffer; - } else if (node.spltType === "str") { - msg.parts.ch = node.splt; - } - var pos = buff.indexOf(node.splt); - var end; - while (pos > -1) { - count++; - end = pos+node.splt.length; - pos = buff.indexOf(node.splt,end); - } - count++; - if (node.stream !== true) { - msg.parts.count = count; - node.c = 0; - } - var i = 0, p = 0; - pos = buff.indexOf(node.splt); - while (pos > -1) { - msg.payload = buff.slice(p,pos); - msg.parts.index = node.c++; - send(RED.util.cloneMessage(msg)); - i++; - p = pos+node.splt.length; - pos = buff.indexOf(node.splt,p); - } - if (count > 1) { - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - if ((node.stream !== true) && (p < buff.length)) { - msg.payload = buff.slice(p,buff.length); - msg.parts.index = node.c++; - msg.parts.count = node.c++; - send(RED.util.cloneMessage(msg)); - node.pendingDones.forEach(d => d()); - node.pendingDones = []; - } - else { - node.buffer = buff.slice(p,buff.length); - node.pendingDones.push(done); - } - if (node.buffer.length == 0) { - done(); - } - } - } else { // otherwise drop the message. - done(); - } - } - }); - } - RED.nodes.registerType("split",SplitNode); - - - var _maxKeptMsgsCount; - - function maxKeptMsgsCount(node) { - if (_maxKeptMsgsCount === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _maxKeptMsgsCount = RED.settings[name]; - } - else { - _maxKeptMsgsCount = 0; - } - } - return _maxKeptMsgsCount; - } - - function applyReduce(exp, accum, msg, index, count, done) { - exp.assign("I", index); - exp.assign("N", count); - exp.assign("A", accum); - RED.util.evaluateJSONataExpression(exp, msg, done); - } - - function exp_or_undefined(exp) { - if((exp === "") || - (exp === null)) { - return undefined; - } - return exp - } - - - function reduceMessageGroup(node,msgInfos,exp,fixup,count,accumulator,done) { - var msgInfo = msgInfos.shift(); - exp.assign("I", msgInfo.msg.parts.index); - exp.assign("N", count); - exp.assign("A", accumulator); - RED.util.evaluateJSONataExpression(exp, msgInfo.msg, (err,result) => { - if (err) { - return done(err); - } - if (msgInfos.length === 0) { - if (fixup) { - fixup.assign("N", count); - fixup.assign("A", result); - RED.util.evaluateJSONataExpression(fixup, {}, (err, result) => { - if (err) { - return done(err); - } - msgInfo.send({payload: result}); - done(); - }); - } else { - msgInfo.send({payload: result}); - done(); - } - } else { - reduceMessageGroup(node,msgInfos,exp,fixup,count,result,done); - } - }); - } - function reduceAndSendGroup(node, group, done) { - var is_right = node.reduce_right; - var flag = is_right ? -1 : 1; - var msgInfos = group.msgs; - const preservedMsgInfos = [...msgInfos]; - try { - RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => { - var reduceExpression = node.reduceExpression; - var fixupExpression = node.fixupExpression; - var count = group.count; - msgInfos.sort(function(x,y) { - var ix = x.msg.parts.index; - var iy = y.msg.parts.index; - if (ix < iy) {return -flag;} - if (ix > iy) {return flag;} - return 0; - }); - reduceMessageGroup(node, msgInfos,reduceExpression,fixupExpression,count,accum,(err,result) => { - if (err) { - preservedMsgInfos.pop(); // omit last message to emit error message - preservedMsgInfos.forEach(mInfo => mInfo.done()); - done(err); - return; - } else { - preservedMsgInfos.forEach(mInfo => mInfo.done()); - done(); - } - }) - }); - } catch(err) { - done(new Error(RED._("join.errors.invalid-expr",{error:err.message}))); - } - } - - function reduceMessage(node, msgInfo, done) { - let msg = msgInfo.msg; - if (msg.hasOwnProperty('parts')) { - var parts = msg.parts; - var pending = node.pending; - var pending_count = node.pending_count; - var gid = msg.parts.id; - var count; - if (!pending.hasOwnProperty(gid)) { - if(parts.hasOwnProperty('count')) { - count = msg.parts.count; - } - pending[gid] = { - count: count, - msgs: [] - }; - } - var group = pending[gid]; - var msgs = group.msgs; - if (parts.hasOwnProperty('count') && (group.count === undefined)) { - group.count = parts.count; - } - msgs.push(msgInfo); - pending_count++; - var completeProcess = function(err) { - if (err) { - return done(err); - } - node.pending_count = pending_count; - var max_msgs = maxKeptMsgsCount(node); - if ((max_msgs > 0) && (pending_count > max_msgs)) { - Object.values(node.pending).forEach(group => { - group.msgs.forEach(mInfo => { - if (mInfo.msg._msgid !== msgInfo.msg._msgid) { - mInfo.done(); - } - }); - }); - node.pending = {}; - node.pending_count = 0; - done(RED._("join.too-many")); - return; - } - return done(); - } - if (msgs.length === group.count) { - delete pending[gid]; - pending_count -= msgs.length; - reduceAndSendGroup(node, group, completeProcess) - } else { - completeProcess(); - } - } else { - msgInfo.send(msg); - msgInfo.done(); - done(); - } - } - - function JoinNode(n) { - RED.nodes.createNode(this,n); - this.mode = n.mode||"auto"; - this.property = n.property||"payload"; - this.propertyType = n.propertyType||"msg"; - if (this.propertyType === 'full') { - this.property = "payload"; - } - this.key = n.key||"topic"; - this.timer = (this.mode === "auto") ? 0 : Number(n.timeout || 0)*1000; - this.count = Number(n.count || 0); - this.joiner = n.joiner||""; - this.joinerType = n.joinerType||"str"; - - this.reduce = (this.mode === "reduce"); - if (this.reduce) { - this.exp_init = n.reduceInit; - this.exp_init_type = n.reduceInitType; - var exp_reduce = n.reduceExp; - var exp_fixup = exp_or_undefined(n.reduceFixup); - this.reduce_right = n.reduceRight; - try { - this.reduceExpression = RED.util.prepareJSONataExpression(exp_reduce, this); - this.fixupExpression = (exp_fixup !== undefined) ? RED.util.prepareJSONataExpression(exp_fixup, this) : undefined; - } catch(e) { - this.error(RED._("join.errors.invalid-expr",{error:e.message})); - return; - } - } - - if (this.joinerType === "str") { - this.joiner = this.joiner.replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0"); - } else if (this.joinerType === "bin") { - var joinArray = JSON.parse(n.joiner || "[]"); - if (Array.isArray(joinArray)) { - this.joiner = Buffer.from(joinArray); - } else { - throw new Error("not an array"); - } - } - - this.build = n.build || "array"; - this.accumulate = n.accumulate || "false"; - - this.output = n.output || "stream"; - this.pending = {}; - this.pending_count = 0; - - //this.topic = n.topic; - var node = this; - var inflight = {}; - - var completeSend = function(partId) { - var group = inflight[partId]; - if (group.timeout) { clearTimeout(group.timeout); } - if ((node.accumulate !== true) || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; } - if (group.type === 'array' && group.arrayLen > 1) { - var newArray = []; - group.payload.forEach(function(n) { - newArray = newArray.concat(n); - }) - group.payload = newArray; - } - else if (group.type === 'buffer') { - var buffers = []; - var bufferLen = 0; - if (group.joinChar !== undefined) { - var joinBuffer = Buffer.from(group.joinChar); - for (var i=0; i 0) { - buffers.push(joinBuffer); - bufferLen += joinBuffer.length; - } - if (!Buffer.isBuffer(group.payload[i])) { - group.payload[i] = Buffer.from(group.payload[i]); - } - buffers.push(group.payload[i]); - bufferLen += group.payload[i].length; - } - } - else { - bufferLen = group.bufferLen; - buffers = group.payload; - } - group.payload = Buffer.concat(buffers,bufferLen); - } - - if (group.type === 'string') { - var groupJoinChar = group.joinChar; - if (typeof group.joinChar !== 'string') { - groupJoinChar = group.joinChar.toString(); - } - RED.util.setMessageProperty(group.msg,node.property,group.payload.join(groupJoinChar)); - } - else { - if (node.propertyType === 'full') { - group.msg = RED.util.cloneMessage(group.msg); - } - RED.util.setMessageProperty(group.msg,node.property,group.payload); - } - if (group.msg.hasOwnProperty('parts') && group.msg.parts.hasOwnProperty('parts')) { - group.msg.parts = group.msg.parts.parts; - } - else { - delete group.msg.parts; - } - delete group.msg.complete; - group.send(RED.util.cloneMessage(group.msg)); - group.dones.forEach(f => f()); - group.dones = []; - } - - var pendingMessages = []; - var activeMessage = null; - // In reduce mode, we must process messages fully in order otherwise - // groups may overlap and cause unexpected results. The use of JSONata - // means some async processing *might* occur if flow/global context is - // accessed. - var processReduceMessageQueue = function(msgInfo) { - if (msgInfo) { - // A new message has arrived - add it to the message queue - pendingMessages.push(msgInfo); - if (activeMessage !== null) { - // The node is currently processing a message, so do nothing - // more with this message - return; - } - } - if (pendingMessages.length === 0) { - // There are no more messages to process, clear the active flag - // and return - activeMessage = null; - return; - } - - // There are more messages to process. Get the next message and - // start processing it. Recurse back in to check for any more - var nextMsgInfo = pendingMessages.shift(); - activeMessage = true; - reduceMessage(node, nextMsgInfo, err => { - if (err) { - nextMsgInfo.done(err);//.error(err,nextMsg); - } - activeMessage = null; - processReduceMessageQueue(); - }) - } - - this.on("input", function(msg, send, done) { - try { - var property; - var partId = "_"; - if (node.propertyType == "full") { - property = msg; - } - else { - try { - property = RED.util.getMessageProperty(msg,node.property); - } catch(err) { - node.warn("Message property "+node.property+" not found"); - done(); - return; - } - } - - if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) { - // if a blank reset messag erest it all. - if (msg.hasOwnProperty("reset")) { - if (inflight && inflight.hasOwnProperty("partId") && inflight[partId].timeout) { - clearTimeout(inflight[partId].timeout); - } - inflight = {}; - } - else { - node.warn("Message missing msg.parts property - cannot join in 'auto' mode") - } - done(); - return; - } - - var payloadType; - var propertyKey; - var targetCount; - var joinChar; - var arrayLen; - var propertyIndex; - if (node.mode === "auto") { - // Use msg.parts to identify all of the group information - partId = msg.parts.id; - payloadType = msg.parts.type; - targetCount = msg.parts.count; - joinChar = msg.parts.ch; - propertyKey = msg.parts.key; - arrayLen = msg.parts.len; - propertyIndex = msg.parts.index; - } - else if (node.mode === 'reduce') { - return processReduceMessageQueue({msg, send, done}); - } - else { - // Use the node configuration to identify all of the group information - payloadType = node.build; - targetCount = node.count; - joinChar = node.joiner; - if (n.count === "" && msg.hasOwnProperty('parts')) { - targetCount = msg.parts.count || 0; - } - if (node.build === 'object') { - propertyKey = RED.util.getMessageProperty(msg,node.key); - } - } - - if (msg.hasOwnProperty("restartTimeout")) { - if (inflight[partId]) { - if (inflight[partId].timeout) { - clearTimeout(inflight[partId].timeout); - } - if (node.timer > 0) { - inflight[partId].timeout = setTimeout(function() { - completeSend(partId) - }, node.timer) - } - } - } - - if (msg.hasOwnProperty("reset")) { - if (inflight[partId]) { - if (inflight[partId].timeout) { - clearTimeout(inflight[partId].timeout); - } - inflight[partId].dones.forEach(f => f()); - delete inflight[partId] - } - done(); - return; - } - - if ((payloadType === 'object') && (propertyKey === null || propertyKey === undefined || propertyKey === "")) { - if (node.mode === "auto") { - node.warn("Message missing 'msg.parts.key' property - cannot add to object"); - } - else { - if (msg.hasOwnProperty('complete')) { - if (inflight[partId]) { - inflight[partId].msg.complete = msg.complete; - inflight[partId].send = send; - completeSend(partId); - } - } - else { - node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object") - } - } - done(); - return; - } - - if (!inflight.hasOwnProperty(partId)) { - if (payloadType === 'object' || payloadType === 'merged') { - inflight[partId] = { - currentCount:0, - payload:{}, - targetCount:targetCount, - type:"object", - msg:RED.util.cloneMessage(msg), - send: send, - dones: [] - }; - } - else { - inflight[partId] = { - currentCount:0, - payload:[], - targetCount:targetCount, - type:payloadType, - msg:RED.util.cloneMessage(msg), - send: send, - dones: [] - }; - if (payloadType === 'string') { - inflight[partId].joinChar = joinChar; - } else if (payloadType === 'array') { - inflight[partId].arrayLen = arrayLen; - } else if (payloadType === 'buffer') { - inflight[partId].bufferLen = 0; - inflight[partId].joinChar = joinChar; - } - } - if (node.timer > 0) { - inflight[partId].timeout = setTimeout(function() { - completeSend(partId) - }, node.timer) - } - } - inflight[partId].dones.push(done); - - var group = inflight[partId]; - if (payloadType === 'buffer') { - if (property !== undefined) { - if (Buffer.isBuffer(property) || (typeof property === "string") || Array.isArray(property)) { - inflight[partId].bufferLen += property.length; - } - else { - done(RED._("join.errors.invalid-type",{error:(typeof property)})); - return; - } - } - } - if (payloadType === 'object') { - group.payload[propertyKey] = property; - group.currentCount = Object.keys(group.payload).length; - } else if (payloadType === 'merged') { - if (Array.isArray(property) || typeof property !== 'object') { - if (!msg.hasOwnProperty("complete")) { - node.warn("Cannot merge non-object types"); - } - } else { - for (propertyKey in property) { - if (property.hasOwnProperty(propertyKey) && propertyKey !== '_msgid') { - group.payload[propertyKey] = property[propertyKey]; - } - } - group.currentCount = Object.keys(group.payload).length; - //group.currentCount++; - } - } else { - if (!isNaN(propertyIndex)) { - if (group.payload[propertyIndex] == undefined) { group.currentCount++; } - group.payload[propertyIndex] = property; - } else { - if (property !== undefined) { - group.payload.push(property); - group.currentCount++; - } - } - } - group.msg = Object.assign(group.msg, msg); - group.send = send; - var tcnt = group.targetCount; - if (msg.hasOwnProperty("parts")) { - tcnt = group.targetCount || msg.parts.count; - group.targetCount = tcnt; - } - if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) { - completeSend(partId); - } - } - catch(err) { - done(err); - console.log(err.stack); - } - }); - - this.on("close", function() { - for (var i in inflight) { - if (inflight.hasOwnProperty(i)) { - clearTimeout(inflight[i].timeout); - inflight[i].dones.forEach(d => d()); - } - } - }); - } - RED.nodes.registerType("join",JoinNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.html b/packages/node_modules/@node-red/nodes/core/sequence/18-sort.html deleted file mode 100644 index fd27c8b54..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.js b/packages/node_modules/@node-red/nodes/core/sequence/18-sort.js deleted file mode 100644 index 3bcdfb105..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/18-sort.js +++ /dev/null @@ -1,266 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var _max_kept_msgs_count; - - function max_kept_msgs_count(node) { - if (_max_kept_msgs_count === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _max_kept_msgs_count = RED.settings[name]; - } - else { - _max_kept_msgs_count = 0; - } - } - return _max_kept_msgs_count; - } - - // function get_context_val(node, name, dval) { - // var context = node.context(); - // var val = context.get(name); - // if (val === undefined) { - // context.set(name, dval); - // return dval; - // } - // return val; - // } - - function SortNode(n) { - RED.nodes.createNode(this, n); - var node = this; - var pending = {};//get_context_val(node, 'pending', {}) - var pending_count = 0; - var pending_id = 0; - var order = n.order || "ascending"; - var as_num = n.as_num || false; - var target_prop = n.target || "payload"; - var target_is_prop = (n.targetType === 'msg'); - var key_is_exp = target_is_prop ? (n.msgKeyType === "jsonata") : (n.seqKeyType === "jsonata"); - var key_prop = n.seqKey || "payload"; - var key_exp = target_is_prop ? n.msgKey : n.seqKey; - - if (key_is_exp) { - try { - key_exp = RED.util.prepareJSONataExpression(key_exp, this); - } - catch (e) { - node.error(RED._("sort.invalid-exp",{message:e.toString()})); - return; - } - } - var dir = (order === "ascending") ? 1 : -1; - var conv = as_num ? function(x) { return Number(x); } - : function(x) { return x; }; - - function generateComparisonFunction(key) { - return function(x, y) { - var xp = conv(key(x)); - var yp = conv(key(y)); - if (xp === yp) { return 0; } - if (xp > yp) { return dir; } - return -dir; - }; - } - - function sortMessageGroup(group) { - var promise; - var msgInfos = group.msgInfos; - if (key_is_exp) { - var evaluatedDataPromises = msgInfos.map(mInfo => { - return new Promise((resolve,reject) => { - RED.util.evaluateJSONataExpression(key_exp, mInfo.msg, (err, result) => { - if (err) { - reject(RED._("sort.invalid-exp",{message:err.toString()})); - } else { - resolve({ - item: mInfo, - sortValue: result - }) - } - }); - }) - }); - promise = Promise.all(evaluatedDataPromises).then(evaluatedElements => { - // Once all of the sort keys are evaluated, sort by them - var comp = generateComparisonFunction(elem=>elem.sortValue); - return evaluatedElements.sort(comp).map(elem=>elem.item); - }); - } else { - var key = function(msg) { - return ; - } - var comp = generateComparisonFunction(mInfo => RED.util.getMessageProperty(mInfo.msg, key_prop)); - try { - msgInfos.sort(comp); - } - catch (e) { - return; // not send when error - } - promise = Promise.resolve(msgInfos); - } - return promise.then(msgInfos => { - for (let i = 0; i < msgInfos.length; i++) { - const msg = msgInfos[i].msg; - msg.parts.index = i; - msgInfos[i].send(msg); - msgInfos[i].done(); - } - }); - } - - function sortMessageProperty(msg) { - var data = RED.util.getMessageProperty(msg, target_prop); - if (Array.isArray(data)) { - if (key_is_exp) { - // key is an expression. Evaluated the expression for each item - // to get its sort value. As this could be async, need to do - // it first. - var evaluatedDataPromises = data.map(elem => { - return new Promise((resolve,reject) => { - RED.util.evaluateJSONataExpression(key_exp, elem, (err, result) => { - if (err) { - reject(RED._("sort.invalid-exp",{message:err.toString()})); - } else { - resolve({ - item: elem, - sortValue: result - }) - } - }); - }) - }) - return Promise.all(evaluatedDataPromises).then(evaluatedElements => { - // Once all of the sort keys are evaluated, sort by them - // and reconstruct the original message item with the newly - // sorted values. - var comp = generateComparisonFunction(elem=>elem.sortValue); - data = evaluatedElements.sort(comp).map(elem=>elem.item); - RED.util.setMessageProperty(msg, target_prop,data); - return true; - }) - } else { - var comp = generateComparisonFunction(elem=>elem); - try { - data.sort(comp); - } catch (e) { - return Promise.resolve(false); - } - return Promise.resolve(true); - } - } - return Promise.resolve(false); - } - - function removeOldestPending() { - var oldest; - var oldest_key; - for(var key in pending) { - if (pending.hasOwnProperty(key)) { - var item = pending[key]; - if((oldest === undefined) || - (oldest.seq_no > item.seq_no)) { - oldest = item; - oldest_key = key; - } - } - } - if(oldest !== undefined) { - oldest.msgInfos[oldest.msgInfos.length - 1].done(RED._("sort.too-many")); - for (let i = 0; i < oldest.msgInfos.length - 1; i++) { - oldest.msgInfos[i].done(); - } - delete pending[oldest_key]; - return oldest.msgInfos.length; - } - return 0; - } - - function processMessage(msgInfo) { - const msg = msgInfo.msg; - if (target_is_prop) { - sortMessageProperty(msg).then(send => { - if (send) { - msgInfo.send(msg); - } - msgInfo.done(); - }).catch(err => { - msgInfo.done(err); - }); - return; - } - var parts = msg.parts; - if (!parts || !parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) { - msgInfo.done(); - return; - } - var gid = parts.id; - if (!pending.hasOwnProperty(gid)) { - pending[gid] = { - count: undefined, - msgInfos: [], - seq_no: pending_id++ - }; - } - var group = pending[gid]; - var msgInfos = group.msgInfos; - msgInfos.push(msgInfo); - if (parts.hasOwnProperty("count")) { - group.count = parts.count; - } - pending_count++; - if (group.count === msgInfos.length) { - delete pending[gid] - sortMessageGroup(group).catch(err => { - // throw an error for last message, and just call done() for remaining messages - msgInfos[msgInfos.length-1].done(err); - for (let i = 0; i < msgInfos.length - 1; i++) { - msgInfos[i].done() - }; - }); - pending_count -= msgInfos.length; - } else { - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (pending_count > max_msgs)) { - pending_count -= removeOldestPending(); - } - } - } - - this.on("input", function(msg, send, done) { - processMessage({msg, send, done}); - }); - - this.on("close", function() { - for(var key in pending) { - if (pending.hasOwnProperty(key)) { - node.log(RED._("sort.clear"), pending[key].msgInfos[0]); - const group = pending[key]; - group.msgInfos.forEach(mInfo => { - mInfo.done(); - }); - delete pending[key]; - } - } - pending_count = 0; - }); - } - - RED.nodes.registerType("sort", SortNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html deleted file mode 100644 index 418ac605b..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js b/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js deleted file mode 100644 index f3f29df6a..000000000 --- a/packages/node_modules/@node-red/nodes/core/sequence/19-batch.js +++ /dev/null @@ -1,311 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var _max_kept_msgs_count = undefined; - - function max_kept_msgs_count(node) { - if (_max_kept_msgs_count === undefined) { - var name = "nodeMessageBufferMaxLength"; - if (RED.settings.hasOwnProperty(name)) { - _max_kept_msgs_count = RED.settings[name]; - } - else { - _max_kept_msgs_count = 0; - } - } - return _max_kept_msgs_count; - } - - function send_msgs(node, msgInfos, clone_msg) { - var count = msgInfos.length; - var msg_id = msgInfos[0].msg._msgid; - for (var i = 0; i < count; i++) { - var msg = clone_msg ? RED.util.cloneMessage(msgInfos[i].msg) : msgInfos[i].msg; - if (!msg.hasOwnProperty("parts")) { - msg.parts = {}; - } - var parts = msg.parts; - parts.id = msg_id; - parts.index = i; - parts.count = count; - msgInfos[i].send(msg); - //msgInfos[i].done(); - } - } - - function send_interval(node, allow_empty_seq) { - let msgInfos = node.pending; - if (msgInfos.length > 0) { - send_msgs(node, msgInfos, false); - msgInfos.forEach(e => e.done()); - node.pending = []; - } - else { - if (allow_empty_seq) { - let mid = RED.util.generateId(); - let msg = { - payload: null, - parts: { - id: mid, - index: 0, - count: 1 - } - }; - node.send(msg); - } - } - } - - function is_complete(pending, topic) { - if (pending.hasOwnProperty(topic)) { - var p_topic = pending[topic]; - var gids = p_topic.gids; - if (gids.length > 0) { - var gid = gids[0]; - var groups = p_topic.groups; - var group = groups[gid]; - return (group.count === group.msgs.length); - } - } - return false; - } - - function get_msgs_of_topic(pending, topic) { - var p_topic = pending[topic]; - var groups = p_topic.groups; - var gids = p_topic.gids; - var gid = gids[0]; - var group = groups[gid]; - return group.msgs; - } - - function remove_topic(pending, topic) { - var p_topic = pending[topic]; - var groups = p_topic.groups; - var gids = p_topic.gids; - var gid = gids.shift(); - delete groups[gid]; - } - - function try_concat(node, pending) { - var topics = node.topics; - for (var topic of topics) { - if (!is_complete(pending, topic)) { - return; - } - } - var msgInfos = []; - for (var topic of topics) { - var t_msgInfos = get_msgs_of_topic(pending, topic); - msgInfos = msgInfos.concat(t_msgInfos); - } - for (var topic of topics) { - remove_topic(pending, topic); - } - send_msgs(node, msgInfos, true); - msgInfos.forEach(e => e.done() ); - node.pending_count -= msgInfos.length; - } - - function add_to_topic_group(pending, topic, gid, msgInfo) { - if (!pending.hasOwnProperty(topic)) { - pending[topic] = { groups: {}, gids: [] }; - } - var p_topic = pending[topic]; - var groups = p_topic.groups; - var gids = p_topic.gids; - if (!groups.hasOwnProperty(gid)) { - groups[gid] = { msgs: [], count: undefined }; - gids.push(gid); - } - var group = groups[gid]; - group.msgs.push(msgInfo); - if ((group.count === undefined) && - msgInfo.msg.parts.hasOwnProperty('count')) { - group.count = msgInfo.msg.parts.count; - } - } - - function concat_msg(node, msg, send, done) { - var topic = msg.topic; - if(node.topics.indexOf(topic) >= 0) { - if (!msg.hasOwnProperty("parts") || - !msg.parts.hasOwnProperty("id") || - !msg.parts.hasOwnProperty("index") || - !msg.parts.hasOwnProperty("count")) { - done(RED._("batch.no-parts")); - return; - } - var gid = msg.parts.id; - var pending = node.pending; - add_to_topic_group(pending, topic, gid, {msg, send, done}); - node.pending_count++; - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - Object.values(node.pending).forEach(p_topic => { - Object.values(p_topic.groups).forEach(group => { - group.msgs.forEach(msgInfo => { - if (msgInfo.msg.id === msg.id) { - // the message that caused the overflow - msgInfo.done(RED._("batch.too-many")); - } else { - msgInfo.done(); - } - }) - }) - }); - node.pending = {}; - node.pending_count = 0; - } - try_concat(node, pending); - } - } - - function BatchNode(n) { - RED.nodes.createNode(this,n); - var node = this; - var mode = n.mode || "count"; - - node.pending_count = 0; - if (mode === "count") { - var count = Number(n.count || 1); - var overlap = Number(n.overlap || 0); - var is_overlap = (overlap > 0); - if (count <= overlap) { - node.error(RED._("batch.count.invalid")); - return; - } - node.pending = []; - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - done(); - return; - } - var queue = node.pending; - queue.push({msg, send, done}); - node.pending_count++; - if (queue.length === count) { - send_msgs(node, queue, is_overlap); - for (let i = 0; i < queue.length-overlap; i++) { - queue[i].done(); - } - node.pending = - (overlap === 0) ? [] : queue.slice(-overlap); - node.pending_count = 0; - } - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - let lastMInfo = node.pending.pop(); - lastMInfo.done(RED._("batch.too-many")); - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - } - }); - this.on("close", function() { - node.pending.forEach(e=> e.done()); - node.pending_count = 0; - node.pending = []; - }); - } - else if (mode === "interval") { - var interval = Number(n.interval || "0") *1000; - var allow_empty_seq = n.allowEmptySequence; - node.pending = [] - function msgHandler() { - send_interval(node, allow_empty_seq); - node.pending_count = 0; - } - var timer = undefined; - if (interval > 0) { - timer = setInterval(msgHandler, interval); - } - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - if (timer !== undefined) { - clearInterval(timer); - } - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - done(); - if (interval > 0) { - timer = setInterval(msgHandler, interval); - } - return; - } - node.pending.push({msg, send, done}); - node.pending_count++; - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - let lastMInfo = node.pending.pop(); - lastMInfo.done(RED._("batch.too-many")); - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - } - }); - this.on("close", function() { - if (timer !== undefined) { - clearInterval(timer); - } - node.pending.forEach(e => e.done()); - node.pending = []; - node.pending_count = 0; - }); - } - else if(mode === "concat") { - node.topics = (n.topics || []).map(function(x) { - return x.topic; - }); - node.pending = {}; - this.on("input", function(msg, send, done) { - if (msg.hasOwnProperty("reset")) { - Object.values(node.pending).forEach(p_topic => { - Object.values(p_topic.groups).forEach(group => { - group.msgs.forEach(e => e.done()); - }); - }); - node.pending = {}; - node.pending_count = 0; - done(); - return; - } - concat_msg(node, msg, send, done); - }); - this.on("close", function() { - Object.values(node.pending).forEach(p_topic => { - Object.values(p_topic.groups).forEach(group => { - group.msgs.forEach(e => e.done()); - }); - }); - node.pending = {}; - node.pending_count = 0; - }); - } - else { - node.error(RED._("batch.unexpected")); - } - } - - RED.nodes.registerType("batch", BatchNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.html b/packages/node_modules/@node-red/nodes/core/storage/10-file.html deleted file mode 100755 index b76a01615..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.html +++ /dev/null @@ -1,347 +0,0 @@ - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.js b/packages/node_modules/@node-red/nodes/core/storage/10-file.js deleted file mode 100644 index 079b4e82f..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.js +++ /dev/null @@ -1,401 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var fs = require("fs-extra"); - var os = require("os"); - var path = require("path"); - var iconv = require("iconv-lite") - - function encode(data, enc) { - if (enc !== "none") { - return iconv.encode(data, enc); - } - return Buffer.from(data); - } - - function decode(data, enc) { - if (enc !== "none") { - return iconv.decode(data, enc); - } - return data.toString(); - } - - function FileNode(n) { - // Write/delete a file - RED.nodes.createNode(this,n); - this.filename = n.filename; - this.appendNewline = n.appendNewline; - this.overwriteFile = n.overwriteFile.toString(); - this.createDir = n.createDir || false; - this.encoding = n.encoding || "none"; - var node = this; - node.wstream = null; - node.msgQueue = []; - node.closing = false; - node.closeCallback = null; - - function processMsg(msg,nodeSend, done) { - var filename = node.filename || msg.filename || ""; - var fullFilename = filename; - if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) { - fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename)); - } - if ((!node.filename) && (!node.tout)) { - node.tout = setTimeout(function() { - node.status({fill:"grey",shape:"dot",text:filename}); - clearTimeout(node.tout); - node.tout = null; - },333); - } - if (filename === "") { - node.warn(RED._("file.errors.nofilename")); - done(); - } else if (node.overwriteFile === "delete") { - fs.unlink(fullFilename, function (err) { - if (err) { - node.error(RED._("file.errors.deletefail",{error:err.toString()}),msg); - } - else { - if (RED.settings.verbose) { - node.log(RED._("file.status.deletedfile",{file:filename})); - } - nodeSend(msg); - } - done(); - }); - } else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) { - var dir = path.dirname(fullFilename); - if (node.createDir) { - try { - fs.ensureDirSync(dir); - } - catch(err) { - node.error(RED._("file.errors.createfail",{error:err.toString()}),msg); - done(); - return; - } - } - - var data = msg.payload; - if ((typeof data === "object") && (!Buffer.isBuffer(data))) { - data = JSON.stringify(data); - } - if (typeof data === "boolean") { data = data.toString(); } - if (typeof data === "number") { data = data.toString(); } - if ((node.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; } - var buf; - if (node.encoding === "setbymsg") { - buf = encode(data, msg.encoding || "none"); - } - else { buf = encode(data, node.encoding); } - if (node.overwriteFile === "true") { - var wstream = fs.createWriteStream(fullFilename, { encoding:'binary', flags:'w', autoClose:true }); - node.wstream = wstream; - wstream.on("error", function(err) { - node.error(RED._("file.errors.writefail",{error:err.toString()}),msg); - done(); - }); - wstream.on("open", function() { - wstream.once("close", function() { - nodeSend(msg); - done(); - }); - wstream.end(buf); - }) - return; - } - else { - // Append mode - var recreateStream = !node.wstream || !node.filename; - if (node.wstream && node.wstreamIno) { - // There is already a stream open and we have the inode - // of the file. Check the file hasn't been deleted - // or deleted and recreated. - try { - var stat = fs.statSync(fullFilename); - // File exists - check the inode matches - if (stat.ino !== node.wstreamIno) { - // The file has been recreated. Close the current - // stream and recreate it - recreateStream = true; - node.wstream.end(); - delete node.wstream; - delete node.wstreamIno; - } - } - catch(err) { - // File does not exist - recreateStream = true; - node.wstream.end(); - delete node.wstream; - delete node.wstreamIno; - } - } - if (recreateStream) { - node.wstream = fs.createWriteStream(fullFilename, { encoding:'binary', flags:'a', autoClose:true }); - node.wstream.on("open", function(fd) { - try { - var stat = fs.statSync(fullFilename); - node.wstreamIno = stat.ino; - } catch(err) { - } - }); - node.wstream.on("error", function(err) { - node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg); - done(); - }); - } - if (node.filename) { - // Static filename - write and reuse the stream next time - node.wstream.write(buf, function() { - nodeSend(msg); - done(); - }); - } - else { - // Dynamic filename - write and close the stream - node.wstream.once("close", function() { - nodeSend(msg); - delete node.wstream; - delete node.wstreamIno; - done(); - }); - node.wstream.end(buf); - } - } - } - else { - done(); - } - } - - function processQueue(queue) { - var event = queue[0]; - processMsg(event.msg, event.send, function() { - event.done(); - queue.shift(); - if (queue.length > 0) { - processQueue(queue); - } - else if (node.closing) { - closeNode(); - } - }); - } - - this.on("input", function(msg,nodeSend,nodeDone) { - var msgQueue = node.msgQueue; - msgQueue.push({ - msg: msg, - send: nodeSend, - done: nodeDone - }) - if (msgQueue.length > 1) { - // pending write exists - return; - } - try { - processQueue(msgQueue); - } - catch (e) { - node.msgQueue = []; - if (node.closing) { - closeNode(); - } - throw e; - } - }); - - function closeNode() { - if (node.wstream) { node.wstream.end(); } - if (node.tout) { clearTimeout(node.tout); } - node.status({}); - var cb = node.closeCallback; - node.closeCallback = null; - node.closing = false; - if (cb) { - cb(); - } - } - - this.on('close', function(done) { - if (node.closing) { - // already closing - return; - } - node.closing = true; - if (done) { - node.closeCallback = done; - } - if (node.msgQueue.length > 0) { - // close after queue processed - return; - } - else { - closeNode(); - } - }); - } - RED.nodes.registerType("file",FileNode); - - - function FileInNode(n) { - // Read a file - RED.nodes.createNode(this,n); - this.filename = n.filename; - this.format = n.format; - this.chunk = false; - this.encoding = n.encoding || "none"; - this.allProps = n.allProps || false; - if (n.sendError === undefined) { - this.sendError = true; - } else { - this.sendError = n.sendError; - } - if (this.format === "lines") { this.chunk = true; } - if (this.format === "stream") { this.chunk = true; } - var node = this; - - this.on("input",function(msg, nodeSend, nodeDone) { - var filename = (node.filename || msg.filename || "").replace(/\t|\r|\n/g,''); - var fullFilename = filename; - if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) { - fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename)); - } - if (!node.filename) { - node.status({fill:"grey",shape:"dot",text:filename}); - } - if (filename === "") { - node.warn(RED._("file.errors.nofilename")); - nodeDone(); - } - else { - msg.filename = filename; - var lines = Buffer.from([]); - var spare = ""; - var count = 0; - var type = "buffer"; - var ch = ""; - if (node.format === "lines") { - ch = "\n"; - type = "string"; - } - var getout = false; - - var rs = fs.createReadStream(fullFilename) - .on('readable', function () { - var chunk; - var m; - var hwm = rs._readableState.highWaterMark; - while (null !== (chunk = rs.read())) { - if (node.chunk === true) { - getout = true; - if (node.format === "lines") { - spare += decode(chunk, node.encoding); - var bits = spare.split("\n"); - for (var i=0; i < bits.length - 1; i++) { - m = {}; - if (node.allProps == true) { - m = RED.util.cloneMessage(msg); - } - else { - m.topic = msg.topic; - m.filename = msg.filename; - } - m.payload = bits[i]; - m.parts= {index:count, ch:ch, type:type, id:msg._msgid} - count += 1; - nodeSend(m); - } - spare = bits[i]; - } - if (node.format === "stream") { - m = {}; - if (node.allProps == true) { - m = RED.util.cloneMessage(msg); - } - else { - m.topic = msg.topic; - m.filename = msg.filename; - } - m.payload = chunk; - m.parts = {index:count, ch:ch, type:type, id:msg._msgid} - count += 1; - if (chunk.length < hwm) { // last chunk is smaller that high water mark = eof - getout = false; - m.parts.count = count; - } - nodeSend(m); - } - } - else { - lines = Buffer.concat([lines,chunk]); - } - } - }) - .on('error', function(err) { - node.error(err, msg); - if (node.sendError) { - var sendMessage = RED.util.cloneMessage(msg); - delete sendMessage.payload; - sendMessage.error = err; - nodeSend(sendMessage); - } - nodeDone(); - }) - .on('end', function() { - if (node.chunk === false) { - if (node.format === "utf8") { - msg.payload = decode(lines, node.encoding); - } - else { msg.payload = lines; } - nodeSend(msg); - } - else if (node.format === "lines") { - var m = {}; - if (node.allProps) { - m = RED.util.cloneMessage(msg); - } - else { - m.topic = msg.topic; - m.filename = msg.filename; - } - m.payload = spare; - m.parts = { - index: count, - count: count + 1, - ch: ch, - type: type, - id: msg._msgid - } - nodeSend(m); - } - else if (getout) { // last chunk same size as high water mark - have to send empty extra packet. - var m = { parts:{index:count, count:count, ch:ch, type:type, id:msg._msgid} }; - nodeSend(m); - } - nodeDone(); - }); - } - }); - this.on('close', function() { - node.status({}); - }); - } - RED.nodes.registerType("file in",FileInNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/storage/23-watch.html b/packages/node_modules/@node-red/nodes/core/storage/23-watch.html deleted file mode 100644 index eafbd46bc..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/23-watch.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/storage/23-watch.js b/packages/node_modules/@node-red/nodes/core/storage/23-watch.js deleted file mode 100644 index 9b6c4bc62..000000000 --- a/packages/node_modules/@node-red/nodes/core/storage/23-watch.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var Notify = require("fs.notify"); - var fs = require("fs"); - var path = require("path"); - - var getAllDirs = function (dir, filelist) { - filelist = filelist || []; - fs.readdirSync(dir).forEach(file => { - try { - if (fs.statSync(path.join(dir, file)).isDirectory() ) { - filelist.push(path.join(dir, file)); - getAllDirs(path.join(dir, file), filelist); - } - } catch (error) { - //should we raise an error? - } - }); - return filelist; - } - - function WatchNode(n) { - RED.nodes.createNode(this,n); - - this.recursive = n.recursive || false; - this.files = (n.files || "").split(","); - for (var f=0; f < this.files.length; f++) { - this.files[f] = this.files[f].trim(); - } - this.p = (this.files.length === 1) ? this.files[0] : JSON.stringify(this.files); - var node = this; - - if (node.recursive) { - for (var fi in node.files) { - if (node.files.hasOwnProperty(fi)) { - node.files = node.files.concat(getAllDirs( node.files[fi])); - } - } - } - - var notifications = new Notify(node.files); - notifications.on('change', function (file, event, fpath) { - var stat; - try { - if (fs.statSync(fpath).isDirectory()) { fpath = path.join(fpath,file); } - stat = fs.statSync(fpath); - } catch(e) { } - var type = "none"; - var msg = { payload:fpath, topic:node.p, file:file, filename:fpath }; - if (stat) { - if (stat.isFile()) { type = "file"; msg.size = stat.size; } - else if (stat.isBlockDevice()) { type = "blockdevice"; } - else if (stat.isCharacterDevice()) { type = "characterdevice"; } - else if (stat.isSocket()) { type = "socket"; } - else if (stat.isFIFO()) { type = "fifo"; } - else if (stat.isDirectory()) { - type = "directory"; - if (node.recursive) { - notifications.add([fpath]); - notifications.add(getAllDirs(fpath)); - } - } - else { type = "n/a"; } - } - msg.type = type; - node.send(msg); - }); - - notifications.on('error', function (error, fpath) { - var msg = { payload:fpath }; - node.error(error,msg); - }); - - this.close = function() { - notifications.close(); - } - } - RED.nodes.registerType("watch",WatchNode); -} diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json b/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json index 85b334e68..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/01 - Output payload value.json @@ -1 +0,0 @@ -[{"id":"87bd706a.aec93","type":"comment","z":"3ae4b3d9.1f77bc","name":"Output payload value to debug sidebar","info":"Debug node can be used to output payload value to debug sidebar.","x":230,"y":60,"wires":[]},{"id":"8035b07f.7547e","type":"inject","z":"3ae4b3d9.1f77bc","name":"","props":[{"p":"payload","v":"Hello, World!","vt":"str"},{"p":"topic","v":"","vt":"string"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":210,"y":100,"wires":[["20d1344f.931e3c"]]},{"id":"20d1344f.931e3c","type":"debug","z":"3ae4b3d9.1f77bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":450,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json b/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json index c8cb9c182..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/02 - Output complete message.json @@ -1 +0,0 @@ -[{"id":"8c66d039.44465","type":"comment","z":"e6956267.5d174","name":"Output complete object","info":"Debug node can be used to output whole object value to debug sidebar.","x":160,"y":60,"wires":[]},{"id":"dac87e40.90376","type":"inject","z":"e6956267.5d174","name":"","props":[{"p":"payload","v":"Hello, World!","vt":"str"},{"p":"topic","v":"Sample","vt":"string"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Sample","payload":"Hello, World!","payloadType":"str","x":220,"y":100,"wires":[["a77fa5e3.fac248"]]},{"id":"a77fa5e3.fac248","type":"debug","z":"e6956267.5d174","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":410,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json b/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json index a5866a723..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/03 - Output to console.json @@ -1 +0,0 @@ -[{"id":"fb1c3ce9.c29ee","type":"comment","z":"395f4b0d.8a8774","name":"Output to console","info":"Debug node can be used to output values to console.","x":130,"y":60,"wires":[]},{"id":"3c24e746.9ff6a8","type":"inject","z":"395f4b0d.8a8774","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":170,"y":100,"wires":[["66cc7b44.82ba74"]]},{"id":"66cc7b44.82ba74","type":"debug","z":"395f4b0d.8a8774","name":"","active":true,"tosidebar":false,"console":true,"tostatus":false,"complete":"payload","targetType":"msg","x":420,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json b/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json index 9f1fb8265..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/04 - Output to node status.json @@ -1 +0,0 @@ -[{"id":"33791e6a.973502","type":"comment","z":"55587092.1f2b4","name":"Output to node status area","info":"Debug node can be used to output values to status area below the node.","x":170,"y":60,"wires":[]},{"id":"a5d8e744.a034e8","type":"inject","z":"55587092.1f2b4","name":"","repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":190,"y":100,"wires":[["b0646a4d.db4bc8"]]},{"id":"b0646a4d.db4bc8","type":"debug","z":"55587092.1f2b4","name":"","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","x":430,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json b/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json index 1fa64e5af..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json +++ b/packages/node_modules/@node-red/nodes/examples/common/debug/05 - Formatting output using JSONata.json @@ -1 +0,0 @@ -[{"id":"30ed0a73.fcef86","type":"comment","z":"61feb619.5e3d68","name":"Formatting output using JSONata","info":"Debug node can format output value using JSONata expression.","x":200,"y":60,"wires":[]},{"id":"6f477e7d.3a8da","type":"inject","z":"61feb619.5e3d68","name":"","props":[{"p":"payload","v":"Hello, World!","vt":"str"},{"p":"topic","v":"Sample","vt":"string"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Sample","payload":"Hello, World!","payloadType":"str","x":220,"y":100,"wires":[["19c9408d.ac6d4f"]]},{"id":"19c9408d.ac6d4f","type":"debug","z":"61feb619.5e3d68","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"\"[\" & topic & \"] \" & payload","targetType":"jsonata","x":420,"y":100,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json b/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json index e3304eb97..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json +++ b/packages/node_modules/@node-red/nodes/examples/common/link/01 - Link within a tab.json @@ -1 +0,0 @@ -[{"id":"a30d20e6.ec6dc","type":"link in","z":"2f2d9fa4.be6fc","name":"","links":["70d6f012.8fe6d"],"x":235,"y":240,"wires":[["6bf52c5c.d301c4"]]},{"id":"70d6f012.8fe6d","type":"link out","z":"2f2d9fa4.be6fc","name":"","links":["a30d20e6.ec6dc"],"x":315,"y":180,"wires":[]},{"id":"353c85ce.993d0a","type":"inject","z":"2f2d9fa4.be6fc","name":"","topic":"","payload":"Hello, World!","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":180,"wires":[["70d6f012.8fe6d"]]},{"id":"6bf52c5c.d301c4","type":"debug","z":"2f2d9fa4.be6fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":370,"y":240,"wires":[]},{"id":"62ea32aa.d73aac","type":"comment","z":"2f2d9fa4.be6fc","name":"Example: Link Node","info":"Output of link out node can be connected to input of link in node. The connection between links in/out is not shown, so the flow representation can be simplified.","x":130,"y":40,"wires":[]},{"id":"85133fcc.e482","type":"comment","z":"2f2d9fa4.be6fc","name":"Link output of inject node to input of debug node","info":"","x":260,"y":100,"wires":[]},{"id":"c588bc36.87fec","type":"comment","z":"2f2d9fa4.be6fc","name":"↓ connect to link in node","info":"","x":410,"y":140,"wires":[]},{"id":"8abca900.6dfe78","type":"comment","z":"2f2d9fa4.be6fc","name":"↑ connect from link out node","info":"","x":340,"y":280,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json b/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json index e92ad38dc..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/02 - Set any property value.json @@ -1 +0,0 @@ -[{"id":"26e7643f.13ebcc","type":"comment","z":"f4cb1920.4d58c8","name":"Set any property value","info":"Change node can set value to any message property.","x":160,"y":60,"wires":[]},{"id":"4da2494d.9aff68","type":"inject","z":"f4cb1920.4d58c8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":120,"wires":[["5111e689.62e838"]]},{"id":"58ea5868.0596e8","type":"debug","z":"f4cb1920.4d58c8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":530,"y":120,"wires":[]},{"id":"5111e689.62e838","type":"change","z":"f4cb1920.4d58c8","name":"set payload & topic","rules":[{"t":"set","p":"payload","pt":"msg","to":"Hello, World!","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Title","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":120,"wires":[["58ea5868.0596e8"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json b/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json index 2c251157a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/03 - Set value using JSONata.json @@ -1 +0,0 @@ -[{"id":"a9039cda.3649e","type":"comment","z":"a808932c.4ca77","name":"Set value using JSONata","info":"Change node can set value to using JSONata expression.","x":170,"y":60,"wires":[]},{"id":"bdcdd579.cfe668","type":"inject","z":"a808932c.4ca77","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":120,"wires":[["28d110a7.ce3e2"]]},{"id":"c6677fa5.8c111","type":"debug","z":"a808932c.4ca77","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":120,"wires":[]},{"id":"28d110a7.ce3e2","type":"change","z":"a808932c.4ca77","name":"use JSONata","rules":[{"t":"set","p":"payload","pt":"msg","to":"Hello","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"payload & \", World!\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":120,"wires":[["c6677fa5.8c111"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json b/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json index 62f7cc509..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/04 - Set value from env var.json @@ -1 +0,0 @@ -[{"id":"e9143349.a64f7","type":"comment","z":"a32e6d69.1b13b","name":"Set value from environment variable","info":"Change node can set value from environment variable.","x":200,"y":60,"wires":[]},{"id":"a7c2725.a631f9","type":"inject","z":"a32e6d69.1b13b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":120,"wires":[["e455c302.2f795"]]},{"id":"6f203119.21895","type":"debug","z":"a32e6d69.1b13b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":120,"wires":[]},{"id":"e455c302.2f795","type":"change","z":"a32e6d69.1b13b","name":"set env var","rules":[{"t":"set","p":"payload","pt":"msg","to":"HOME","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":120,"wires":[["6f203119.21895"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json b/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json index 8424b1d2b..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json +++ b/packages/node_modules/@node-red/nodes/examples/function/change/05 - Set flow context.json @@ -1 +0,0 @@ -[{"id":"6ecac54d.c43ffc","type":"comment","z":"87ace6c0.f01da8","name":"Set flow context","info":"Change node can set flow context.","x":140,"y":60,"wires":[]},{"id":"80e966d3.9d7a78","type":"inject","z":"87ace6c0.f01da8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":234,"wires":[["abaee298.2d77e"]]},{"id":"60ab671d.b0bbf8","type":"debug","z":"87ace6c0.f01da8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":234,"wires":[]},{"id":"abaee298.2d77e","type":"change","z":"87ace6c0.f01da8","name":"increment count","rules":[{"t":"set","p":"count","pt":"flow","to":"$flowContext(\"count\")+1\t","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"count","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":234,"wires":[["60ab671d.b0bbf8"]]},{"id":"2de2bb38.f20ff4","type":"inject","z":"87ace6c0.f01da8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":140,"wires":[["7b96521e.a3cb0c"]]},{"id":"597b63cd.b3218c","type":"debug","z":"87ace6c0.f01da8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":140,"wires":[]},{"id":"7b96521e.a3cb0c","type":"change","z":"87ace6c0.f01da8","name":"set count to 0","rules":[{"t":"set","p":"count","pt":"flow","to":"0","tot":"num"},{"t":"set","p":"payload","pt":"msg","to":"count","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":140,"wires":[["597b63cd.b3218c"]]},{"id":"3d8cdc6d.2620e4","type":"comment","z":"87ace6c0.f01da8","name":"↓ Initialize","info":"","x":200,"y":100,"wires":[]},{"id":"d8069121.80de7","type":"comment","z":"87ace6c0.f01da8","name":"↓ Count up","info":"","x":200,"y":194,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json b/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json index 849c6d9b4..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/01 - Delay message.json @@ -1 +0,0 @@ -[{"id":"87bd706a.aec93","type":"comment","z":"3ae4b3d9.1f77bc","name":"Delay message","info":"Delay node can delay sending input message to output port by a specified amount of time.","x":160,"y":60,"wires":[]},{"id":"1d17715c.34170f","type":"inject","z":"3ae4b3d9.1f77bc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":210,"y":120,"wires":[["26b43de5.4df8f2","9930fecd.ee0c8"]]},{"id":"9930fecd.ee0c8","type":"debug","z":"3ae4b3d9.1f77bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":410,"y":120,"wires":[]},{"id":"26b43de5.4df8f2","type":"delay","z":"3ae4b3d9.1f77bc","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":400,"y":180,"wires":[["c8c2796c.dcb9f8"]]},{"id":"c8c2796c.dcb9f8","type":"change","z":"3ae4b3d9.1f77bc","name":"Goodbye, World!","rules":[{"t":"set","p":"payload","pt":"msg","to":"Goodbye, World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":180,"wires":[["c58a290e.2fa438"]]},{"id":"c58a290e.2fa438","type":"debug","z":"3ae4b3d9.1f77bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":790,"y":180,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json b/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json index 9b11ad0d3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/02 - Delay message by message property.json @@ -1 +0,0 @@ -[{"id":"c15b8c3e.955ed","type":"comment","z":"6f1773ed.b7c2fc","name":"Delay message by message property","info":"Delay node can delay sending input message to output port by a specified amount of time by `msg.delay` property.","x":210,"y":60,"wires":[]},{"id":"a5ed5817.9df448","type":"inject","z":"6f1773ed.b7c2fc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"delay","v":"1000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"delay 1s","payloadType":"str","x":180,"y":120,"wires":[["5cf53f4.25b7ec","59b7b67a.a8e888"]]},{"id":"59b7b67a.a8e888","type":"debug","z":"6f1773ed.b7c2fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":390,"y":120,"wires":[]},{"id":"5cf53f4.25b7ec","type":"delay","z":"6f1773ed.b7c2fc","name":"","pauseType":"delayv","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":380,"y":180,"wires":[["fc989f41.c4114"]]},{"id":"fc989f41.c4114","type":"change","z":"6f1773ed.b7c2fc","name":"Goodbye, World!","rules":[{"t":"set","p":"payload","pt":"msg","to":"Goodbye, World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":180,"wires":[["74ba3d1c.666034"]]},{"id":"74ba3d1c.666034","type":"debug","z":"6f1773ed.b7c2fc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":770,"y":180,"wires":[]},{"id":"6cdf7297.bf5a8c","type":"inject","z":"6f1773ed.b7c2fc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"delay","v":"10000","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"delay 10s","payloadType":"str","x":180,"y":180,"wires":[["59b7b67a.a8e888","5cf53f4.25b7ec"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json b/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json index 0a4e82e50..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/03 - Reset or flush pending message.json @@ -1 +0,0 @@ -[{"id":"b0200f61.5efa5","type":"comment","z":"86a4fcf3.9f442","name":"Reset or flush pending message","info":"Delay node can reset or flush delayed message by sending it a message with `reset` or `flush` property.","x":170,"y":60,"wires":[]},{"id":"d5cd8991.e6d2e8","type":"inject","z":"86a4fcf3.9f442","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello, World!","payloadType":"str","x":170,"y":120,"wires":[["607f556b.3ec5fc","fd14cb.2044db38"]]},{"id":"fd14cb.2044db38","type":"debug","z":"86a4fcf3.9f442","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":370,"y":120,"wires":[]},{"id":"607f556b.3ec5fc","type":"delay","z":"86a4fcf3.9f442","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":360,"y":180,"wires":[["d1fc6763.2a30c8"]]},{"id":"d1fc6763.2a30c8","type":"change","z":"86a4fcf3.9f442","name":"Goodbye, World!","rules":[{"t":"set","p":"payload","pt":"msg","to":"Goodbye, World!","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":180,"wires":[["15486d4a.80c6f3"]]},{"id":"15486d4a.80c6f3","type":"debug","z":"86a4fcf3.9f442","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":750,"y":180,"wires":[]},{"id":"2b8b28c7.4c8978","type":"inject","z":"86a4fcf3.9f442","name":"reset","props":[{"p":"topic","vt":"str"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":180,"wires":[["607f556b.3ec5fc"]]},{"id":"3a7e1bec.8bc3d4","type":"inject","z":"86a4fcf3.9f442","name":"flush","props":[{"p":"topic","vt":"str"},{"p":"flush","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":240,"wires":[["607f556b.3ec5fc"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json b/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json index 8d6bc7518..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json +++ b/packages/node_modules/@node-red/nodes/examples/function/delay/05 - Limit message rate for each topic.json @@ -1 +0,0 @@ -[{"id":"3dc5015b.96c97e","type":"comment","z":"73c00795.a13908","name":"Limit rate of message transfer for each topic","info":"Delay node can limit of message transmission from input to output port by a specified number of message per a specified time.\nIf `For each topic` is selected, messages are grouped by `msg.topic` value. When grouping messages by topic, intermediate messages are dropped and the last messages received sent.","x":210,"y":60,"wires":[]},{"id":"bdafe4c6.4d5658","type":"inject","z":"73c00795.a13908","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"topic\":\"apple\",\"payload\":1},{\"topic\":\"apple\",\"payload\":2},{\"topic\":\"apple\",\"payload\":3},{\"topic\":\"orange\",\"payload\":1},{\"topic\":\"orange\",\"payload\":2},{\"topic\":\"orange\",\"payload\":3},{\"topic\":\"banana\",\"payload\":1},{\"topic\":\"banana\",\"payload\":2},{\"topic\":\"banana\",\"payload\":3}]","payloadType":"json","x":150,"y":120,"wires":[["f86dc462.195818"]]},{"id":"e0bdfcc1.cdf48","type":"delay","z":"73c00795.a13908","name":"","pauseType":"timed","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":690,"y":120,"wires":[["29d8beea.6b37f2"]]},{"id":"29d8beea.6b37f2","type":"debug","z":"73c00795.a13908","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":890,"y":120,"wires":[]},{"id":"f86dc462.195818","type":"split","z":"73c00795.a13908","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":290,"y":120,"wires":[["9feb3aac.616c38"]]},{"id":"9feb3aac.616c38","type":"change","z":"73c00795.a13908","name":"set topic&payload","rules":[{"t":"set","p":"topic","pt":"msg","to":"payload.topic","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"payload.payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":120,"wires":[["e0bdfcc1.cdf48"]]},{"id":"949199e8.89bc88","type":"comment","z":"73c00795.a13908","name":"↑ pass last message of each topic","info":"","x":740,"y":160,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json b/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json index 365e4405c..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json +++ b/packages/node_modules/@node-red/nodes/examples/function/exec/01 - Get standard output from external command.json @@ -1 +0,0 @@ -[{"id":"6a5f26a9.0cc2d8","type":"inject","z":"835cc8cc.b8cca8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"Hello World!","payloadType":"str","x":190,"y":160,"wires":[["fc2b343c.bbe2f8"]]},{"id":"fc2b343c.bbe2f8","type":"exec","z":"835cc8cc.b8cca8","command":"echo","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":330,"y":160,"wires":[["2f3bcd73.6fedf2"],[],["3280586e.4e3d28"]]},{"id":"2f3bcd73.6fedf2","type":"debug","z":"835cc8cc.b8cca8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":510,"y":160,"wires":[]},{"id":"2b7bc9f1.ab36a6","type":"comment","z":"835cc8cc.b8cca8","name":"Execute external command appending additional args","info":"Exec node can execute external command and can receive its standard output as a payload of first message. Standard error output can be received from second message. The exit code of the command can be obtained from `code` property of third message payload.\n\nIf `Append msg.payload` checkbox is selected, payload value of the input message is appended to command string.\n","x":260,"y":60,"wires":[]},{"id":"3280586e.4e3d28","type":"debug","z":"835cc8cc.b8cca8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":510,"y":220,"wires":[]},{"id":"feba83da.8f227","type":"comment","z":"835cc8cc.b8cca8","name":"↓ execute echo command","info":"","x":390,"y":115,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json b/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json index 992b39df3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json +++ b/packages/node_modules/@node-red/nodes/examples/function/exec/02 - Get error output from external command.json @@ -1 +0,0 @@ -[{"id":"f507b27c.fff1","type":"inject","z":"462f83a6.d3c3cc","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":180,"wires":[["28b4f75a.eae548"]]},{"id":"28b4f75a.eae548","type":"exec","z":"462f83a6.d3c3cc","command":"/non/existing/command","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":350,"y":180,"wires":[[],["27992c1f.a1c964"],["6e7ff001.2412d"]]},{"id":"6e7ff001.2412d","type":"debug","z":"462f83a6.d3c3cc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":240,"wires":[]},{"id":"27992c1f.a1c964","type":"debug","z":"462f83a6.d3c3cc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":570,"y":180,"wires":[]},{"id":"77e85c7c.a560d4","type":"comment","z":"462f83a6.d3c3cc","name":"Execute external command and get error output","info":"Exec node can execute external command and can receive its standard output as a payload of first message. Standard error output can be received from second message. The exit code of the command can be obtained from `code` property of third message payload.\n","x":220,"y":80,"wires":[]},{"id":"c188c28c.f7d34","type":"comment","z":"462f83a6.d3c3cc","name":"↓ try to execute non-existing command","info":"","x":390,"y":134,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json b/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json index f45eb6991..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json +++ b/packages/node_modules/@node-red/nodes/examples/function/range/02 - Scale input and round to integer.json @@ -1 +0,0 @@ -[{"id":"990f33d3.3ffe2","type":"comment","z":"db89ae0c.f52b5","name":"Scale input value & round result to integer","info":"Range node can map input value to output value according to mapping specification.\nThe result value is rounded to nearest integer if `Round result to the nearest integer?` checkbox is checked.","x":240,"y":60,"wires":[]},{"id":"b2639501.5ad638","type":"inject","z":"db89ae0c.f52b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":190,"y":120,"wires":[["dc4509ee.773038"]]},{"id":"dc4509ee.773038","type":"range","z":"db89ae0c.f52b5","minin":"0","maxin":"9","minout":"0","maxout":"128","action":"scale","round":true,"property":"payload","name":"","x":360,"y":120,"wires":[["50991e7e.2ed24"]]},{"id":"50991e7e.2ed24","type":"debug","z":"db89ae0c.f52b5","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":520,"y":120,"wires":[]},{"id":"335e877f.d24e98","type":"inject","z":"db89ae0c.f52b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":190,"y":160,"wires":[["dc4509ee.773038"]]},{"id":"5d88207d.4189","type":"inject","z":"db89ae0c.f52b5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"99","payloadType":"num","x":190,"y":200,"wires":[["dc4509ee.773038"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json b/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json index c637c86f9..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json +++ b/packages/node_modules/@node-red/nodes/examples/function/range/03 - Limit input.json @@ -1 +0,0 @@ -[{"id":"f0bc6a80.d52578","type":"comment","z":"bdb79f11.23e1d","name":"Limit input value","info":"Range node can map input value to output value according to mapping specification.\nThe result value is limited to specified range if `Scale and limit to the target range` is selected.","x":140,"y":60,"wires":[]},{"id":"69652849.749198","type":"inject","z":"bdb79f11.23e1d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":120,"wires":[["104f6f14.1c72e1"]]},{"id":"104f6f14.1c72e1","type":"range","z":"bdb79f11.23e1d","minin":"0","maxin":"100","minout":"0","maxout":"90","action":"clamp","round":false,"property":"payload","name":"","x":330,"y":120,"wires":[["10fcbed4.9c8d71"]]},{"id":"10fcbed4.9c8d71","type":"debug","z":"bdb79f11.23e1d","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":120,"wires":[]},{"id":"93bb4526.7d6e28","type":"inject","z":"bdb79f11.23e1d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":170,"y":160,"wires":[["104f6f14.1c72e1"]]},{"id":"6892dfd8.42386","type":"inject","z":"bdb79f11.23e1d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"num","x":170,"y":200,"wires":[["104f6f14.1c72e1"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json b/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json index ce8cb733a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json +++ b/packages/node_modules/@node-red/nodes/examples/function/range/04 - Scale and wrap input.json @@ -1 +0,0 @@ -[{"id":"808e354d.8de148","type":"comment","z":"b1446352.d689e","name":"Scale and wrap input value","info":"Range node can map input value to output value according to mapping specification.\nThe result value is wrapped if `Scale and wrap within the target range` is selected.","x":170,"y":60,"wires":[]},{"id":"2f033c72.51dcc4","type":"inject","z":"b1446352.d689e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":120,"wires":[["cdaa82c4.820de"]]},{"id":"cdaa82c4.820de","type":"range","z":"b1446352.d689e","minin":"0","maxin":"9","minout":"0","maxout":"90","action":"roll","round":false,"property":"payload","name":"","x":330,"y":120,"wires":[["77f8872b.2a9968"]]},{"id":"77f8872b.2a9968","type":"debug","z":"b1446352.d689e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":500,"y":120,"wires":[]},{"id":"84ac6a7e.77b8d8","type":"inject","z":"b1446352.d689e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":170,"y":160,"wires":[["cdaa82c4.820de"]]},{"id":"92f554c3.b08ad8","type":"inject","z":"b1446352.d689e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"num","x":170,"y":200,"wires":[["cdaa82c4.820de"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json b/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json index 8a51a99f1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/02 - Check all rules.json @@ -1 +0,0 @@ -[{"id":"6ec19fc7.a32ae","type":"comment","z":"e482b0ab.b1b43","name":"Check all rules","info":"Switch node apply all rules if `checking all rules` is selected in settings panel.","x":120,"y":60,"wires":[]},{"id":"1644e138.f8d1ef","type":"switch","z":"e482b0ab.b1b43","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lt","v":"10","vt":"num"},{"t":"gt","v":"-10","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":290,"y":140,"wires":[["624b4e9f.37fee"],["a89d6432.b68318"]]},{"id":"a7f64dbf.3e27b","type":"debug","z":"e482b0ab.b1b43","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":100,"wires":[]},{"id":"a89d6432.b68318","type":"change","z":"e482b0ab.b1b43","name":">-10","rules":[{"t":"set","p":"payload","pt":"msg","to":">-10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":180,"wires":[["f1136da2.23516"]]},{"id":"624b4e9f.37fee","type":"change","z":"e482b0ab.b1b43","name":"<10","rules":[{"t":"set","p":"payload","pt":"msg","to":"<10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":100,"wires":[["a7f64dbf.3e27b"]]},{"id":"f1136da2.23516","type":"debug","z":"e482b0ab.b1b43","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":180,"wires":[]},{"id":"c7e952e9.88e5e","type":"inject","z":"e482b0ab.b1b43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"-10","payloadType":"num","x":150,"y":100,"wires":[["1644e138.f8d1ef"]]},{"id":"8cf8babd.b43db8","type":"inject","z":"e482b0ab.b1b43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":150,"y":180,"wires":[["1644e138.f8d1ef"]]},{"id":"6a43ae86.b92ed","type":"inject","z":"e482b0ab.b1b43","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":150,"y":140,"wires":[["1644e138.f8d1ef"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json b/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json index 0b41e75e3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/03 - Stop after first match.json @@ -1 +0,0 @@ -[{"id":"a12a5708.195688","type":"comment","z":"74b5d6de.372b18","name":"Stop after first match","info":"Switch node stops application of rules if `stopping after first match` is selected in settings panel and a rule evaluates to `true`.","x":160,"y":60,"wires":[]},{"id":"8aebdebf.5d7f2","type":"switch","z":"74b5d6de.372b18","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lt","v":"10","vt":"num"},{"t":"gt","v":"-10","vt":"num"}],"checkall":"false","repair":false,"outputs":2,"x":310,"y":140,"wires":[["5d1851c.a9c5db"],["e8228605.f32018"]]},{"id":"60248c98.69fd44","type":"debug","z":"74b5d6de.372b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":100,"wires":[]},{"id":"e8228605.f32018","type":"change","z":"74b5d6de.372b18","name":">-10","rules":[{"t":"set","p":"payload","pt":"msg","to":">-10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":180,"wires":[["e9a51608.c322b8"]]},{"id":"5d1851c.a9c5db","type":"change","z":"74b5d6de.372b18","name":"<10","rules":[{"t":"set","p":"payload","pt":"msg","to":"<10","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":100,"wires":[["60248c98.69fd44"]]},{"id":"e9a51608.c322b8","type":"debug","z":"74b5d6de.372b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":180,"wires":[]},{"id":"49222b4b.647a84","type":"inject","z":"74b5d6de.372b18","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"-10","payloadType":"num","x":170,"y":100,"wires":[["8aebdebf.5d7f2"]]},{"id":"39e8c133.a56f7e","type":"inject","z":"74b5d6de.372b18","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":170,"y":180,"wires":[["8aebdebf.5d7f2"]]},{"id":"3a22ec96.965a14","type":"inject","z":"74b5d6de.372b18","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":170,"y":140,"wires":[["8aebdebf.5d7f2"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json b/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json index b5e386fa1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/04 - Select output port by type.json @@ -1 +0,0 @@ -[{"id":"6dfd4381.eae2ec","type":"comment","z":"7d002985.82f928","name":"Select output based on type of payload value","info":"Switch node can route input message based on type of payload value.","x":210,"y":40,"wires":[]},{"id":"b139dacf.a0d818","type":"switch","z":"7d002985.82f928","name":"","property":"payload","propertyType":"msg","rules":[{"t":"istype","v":"string","vt":"string"},{"t":"istype","v":"number","vt":"number"}],"checkall":"false","repair":false,"outputs":2,"x":330,"y":120,"wires":[["7c5cd60c.e66b28"],["86cf5262.20f18"]]},{"id":"ca403f12.477a8","type":"debug","z":"7d002985.82f928","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":160,"wires":[]},{"id":"7c5cd60c.e66b28","type":"change","z":"7d002985.82f928","name":"String","rules":[{"t":"set","p":"payload","pt":"msg","to":"string","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":80,"wires":[["d9669648.1177e8"]]},{"id":"86cf5262.20f18","type":"change","z":"7d002985.82f928","name":"Number","rules":[{"t":"set","p":"payload","pt":"msg","to":"number","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":160,"wires":[["ca403f12.477a8"]]},{"id":"d9669648.1177e8","type":"debug","z":"7d002985.82f928","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":80,"wires":[]},{"id":"d3f1c718.dc67e8","type":"inject","z":"7d002985.82f928","name":"Number:128","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"128","payloadType":"num","x":170,"y":80,"wires":[["b139dacf.a0d818"]]},{"id":"a59e275e.6d48c8","type":"inject","z":"7d002985.82f928","name":"String:128","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"128","payloadType":"str","x":160,"y":160,"wires":[["b139dacf.a0d818"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json b/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json index f0955a3f1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/05 - Use JSONata for switch rule.json @@ -1 +0,0 @@ -[{"id":"175ceb0d.8dbe45","type":"comment","z":"17f634f4.e4bc9b","name":"Use JSONata expression for rules","info":"Switch node can use JSONata expression for calculating complex conditions.","x":200,"y":60,"wires":[]},{"id":"d89491c3.793a3","type":"switch","z":"17f634f4.e4bc9b","name":"","property":"payload","propertyType":"msg","rules":[{"t":"jsonata_exp","v":"(payload % 2) = 0","vt":"jsonata"},{"t":"jsonata_exp","v":"(payload % 2) = 1","vt":"jsonata"}],"checkall":"false","repair":false,"outputs":2,"x":310,"y":140,"wires":[["d6cb78a6.872908"],["1f0c62bb.c3d52d"]]},{"id":"9ae9a2aa.a895c","type":"debug","z":"17f634f4.e4bc9b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":100,"wires":[]},{"id":"1f0c62bb.c3d52d","type":"change","z":"17f634f4.e4bc9b","name":"Odd","rules":[{"t":"set","p":"payload","pt":"msg","to":"odd","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":180,"wires":[["34b0a9fb.bafdb6"]]},{"id":"d6cb78a6.872908","type":"change","z":"17f634f4.e4bc9b","name":"Even","rules":[{"t":"set","p":"payload","pt":"msg","to":"even","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":100,"wires":[["9ae9a2aa.a895c"]]},{"id":"34b0a9fb.bafdb6","type":"debug","z":"17f634f4.e4bc9b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":610,"y":180,"wires":[]},{"id":"7beb0333.a55bac","type":"inject","z":"17f634f4.e4bc9b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"7","payloadType":"num","x":170,"y":100,"wires":[["d89491c3.793a3"]]},{"id":"a8db47cf.58ba18","type":"inject","z":"17f634f4.e4bc9b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"8","payloadType":"num","x":170,"y":180,"wires":[["d89491c3.793a3"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json b/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json index 3404b4ff9..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json +++ b/packages/node_modules/@node-red/nodes/examples/function/switch/06 - Use JSONata for switch value.json @@ -1 +0,0 @@ -[{"id":"1c09dc33.934504","type":"comment","z":"166069bc.648516","name":"Use JSONata expression for switch value","info":"Switch node can use JSONata expression for calculating complex switch value.","x":200,"y":60,"wires":[]},{"id":"c7ca4974.d638f8","type":"switch","z":"166069bc.648516","name":"","property":"(payload % 2) = 0","propertyType":"jsonata","rules":[{"t":"true"},{"t":"false"}],"checkall":"false","repair":false,"outputs":2,"x":290,"y":140,"wires":[["ac921b1d.c0dbe8"],["89adcfcc.53d6d"]]},{"id":"ab8972e0.e98b7","type":"debug","z":"166069bc.648516","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":100,"wires":[]},{"id":"89adcfcc.53d6d","type":"change","z":"166069bc.648516","name":"Odd","rules":[{"t":"set","p":"payload","pt":"msg","to":"odd","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":180,"wires":[["426c68cf.64f0e8"]]},{"id":"ac921b1d.c0dbe8","type":"change","z":"166069bc.648516","name":"Even","rules":[{"t":"set","p":"payload","pt":"msg","to":"even","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":100,"wires":[["ab8972e0.e98b7"]]},{"id":"426c68cf.64f0e8","type":"debug","z":"166069bc.648516","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":180,"wires":[]},{"id":"63377f1c.2fbfc","type":"inject","z":"166069bc.648516","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"7","payloadType":"num","x":150,"y":100,"wires":[["c7ca4974.d638f8"]]},{"id":"ea2fa596.ff1638","type":"inject","z":"166069bc.648516","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"8","payloadType":"num","x":150,"y":180,"wires":[["c7ca4974.d638f8"]]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json b/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json index de123bbe8..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json +++ b/packages/node_modules/@node-red/nodes/examples/function/template/01 - Use mustache syntax.json @@ -1 +0,0 @@ -[{"id":"eaf91a6b.a55da8","type":"comment","z":"73a69428.bf4fec","name":"Advanced mustache example","info":"Template node can create a string value using [Mustache](http://mustache.github.io/mustache.5.html) syntax.","x":200,"y":80,"wires":[]},{"id":"61fbfe34.14a02","type":"inject","z":"73a69428.bf4fec","name":"Price of fruits","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"Fruits","payload":"[{\"name\":\"apple\",\"price\":100},{\"name\":\"orange\",\"price\":80},{\"name\":\"banana\",\"price\":210}]","payloadType":"json","x":210,"y":140,"wires":[["bf0cb02.d8e4b5"]]},{"id":"bf0cb02.d8e4b5","type":"template","z":"73a69428.bf4fec","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"# Price List of {{topic}}\n\n{{! outputs list of prices }}\n{{#payload}}\n- {{name}}: {{price}}\n{{/payload}}\n","output":"str","x":380,"y":140,"wires":[["153eb0ff.5622df"]]},{"id":"153eb0ff.5622df","type":"debug","z":"73a69428.bf4fec","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":550,"y":140,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json b/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json index d2042aa98..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json +++ b/packages/node_modules/@node-red/nodes/examples/function/template/02 - Parse result as JSON.json @@ -1 +0,0 @@ -[{"id":"fe821493.2e0e28","type":"comment","z":"1e6bd604.afc8fa","name":"Parse result as JSON","info":"Template node can create a string value using [Mustache](http://mustache.github.io/mustache.5.html) syntax.\nIf `Partsed JSON` output is selected, the created string is parsed as JSON format and JavaScript object is send as an output payload value.","x":160,"y":60,"wires":[]},{"id":"931f94e8.592cd8","type":"inject","z":"1e6bd604.afc8fa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"message","payload":"Hello, World!","payloadType":"str","x":220,"y":120,"wires":[["bb2b0dad.b24b5"]]},{"id":"bb2b0dad.b24b5","type":"template","z":"1e6bd604.afc8fa","name":"JSON template","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"{\n \"key\" : \"{{topic}}\",\n \"value\": \"{{payload}}\"\n}\n","output":"json","x":440,"y":120,"wires":[["baf2e48.2b97418"]]},{"id":"baf2e48.2b97418","type":"debug","z":"1e6bd604.afc8fa","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":120,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json b/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json index dc01b1e3a..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json +++ b/packages/node_modules/@node-red/nodes/examples/function/template/03 - Parse result as YAML.json @@ -1 +0,0 @@ -[{"id":"6ad06659.a1e4e8","type":"comment","z":"369312e8.ba755e","name":"Parse result as YAML","info":"Template node can create a string value using [Mustache](http://mustache.github.io/mustache.5.html) syntax.\nIf `Partsed YAML` output is selected, the created string is parsed as YAML format and JavaScript object is send as an output payload value.","x":180,"y":60,"wires":[]},{"id":"8d6be9a2.c3fa58","type":"inject","z":"369312e8.ba755e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"message","payload":"Hello, World!","payloadType":"str","x":240,"y":120,"wires":[["69369c3.4a98164"]]},{"id":"69369c3.4a98164","type":"template","z":"369312e8.ba755e","name":"YAML template","field":"payload","fieldType":"msg","format":"yaml","syntax":"mustache","template":"key: {{topic}}\nvalue: {{payload}}","output":"yaml","x":460,"y":120,"wires":[["11fb2934.f5de27"]]},{"id":"11fb2934.f5de27","type":"debug","z":"369312e8.ba755e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":650,"y":120,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json b/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json index 401e6c681..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json +++ b/packages/node_modules/@node-red/nodes/examples/function/trigger/01 - Outputs two value with interval.json @@ -1 +0,0 @@ -[{"id":"ec5a531b.68b65","type":"inject","z":"90acd374.2feda","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":100,"wires":[["cb5e0c78.4bf3d"]]},{"id":"1b0f8c3e.1fd7e4","type":"debug","z":"90acd374.2feda","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":490,"y":100,"wires":[]},{"id":"cb5e0c78.4bf3d","type":"trigger","z":"90acd374.2feda","name":"","op1":"1","op2":"0","op1type":"str","op2type":"str","duration":"2","extend":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":320,"y":100,"wires":[["1b0f8c3e.1fd7e4"]]},{"id":"4e5bf6b2.b4dd58","type":"comment","z":"90acd374.2feda","name":"Oputputs two values with interval","info":"Outputs 1. Then output 0 after a certain period of time.\n\n*This could be used, for example, to blink an LED attached to a Raspberry Pi GPIO pin.*","x":170,"y":40,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json b/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json index 0a60fa95d..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json +++ b/packages/node_modules/@node-red/nodes/examples/network/http/03 - Handle URL parameters.json @@ -1,135 +0,0 @@ -[ - { - "id": "9b2d7459.8dd598", - "type": "http in", - "z": "d41b4dd3.ecd6a", - "name": "", - "url": "/hello-param/:name", - "method": "get", - "upload": false, - "swaggerDoc": "", - "x": 290, - "y": 900, - "wires": [ - [ - "83753c80.5e271" - ] - ] - }, - { - "id": "7fe50f46.46209", - "type": "http response", - "z": "d41b4dd3.ecd6a", - "name": "", - "statusCode": "", - "headers": {}, - "x": 590, - "y": 900, - "wires": [] - }, - { - "id": "6e88d2b.828a92c", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Handle URL parameters in an HTTP endpoint", - "info": "Named path parameters (e.g. `:name`) in the URL property can be used to identify parts of the path that can vary between requests.\n\nThe `msg.req.params` property is an object of key/value pairs for each path parameter.", - "x": 290, - "y": 860, - "wires": [] - }, - { - "id": "214bc398.b3482c", - "type": "http request", - "z": "d41b4dd3.ecd6a", - "name": "", - "method": "GET", - "ret": "txt", - "paytoqs": "ignore", - "url": "http://localhost:1880/hello-param/Nick", - "tls": "", - "persist": false, - "proxy": "", - "authType": "", - "x": 390, - "y": 1000, - "wires": [ - [ - "70c0eba4.5f0dc4" - ] - ] - }, - { - "id": "70c0eba4.5f0dc4", - "type": "debug", - "z": "d41b4dd3.ecd6a", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 550, - "y": 1000, - "wires": [] - }, - { - "id": "83753c80.5e271", - "type": "template", - "z": "d41b4dd3.ecd6a", - "name": "page", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "mustache", - "template": "\n \n \n

    Hello {{req.params.name}}!

    \n \n", - "output": "str", - "x": 470, - "y": 900, - "wires": [ - [ - "7fe50f46.46209" - ] - ] - }, - { - "id": "89523bfe.6e5c18", - "type": "inject", - "z": "d41b4dd3.ecd6a", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 240, - "y": 1000, - "wires": [ - [ - "214bc398.b3482c" - ] - ] - }, - { - "id": "6276a6cd.5c3b18", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Send HTTP GET request: http://localhost:1880/hello-param/Nick", - "info": "`http request` node can be used to send **HTTP GET** request.", - "x": 350, - "y": 960, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json b/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json index aec360a91..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json +++ b/packages/node_modules/@node-red/nodes/examples/network/http/06 - Post file to a flow.json @@ -1,126 +0,0 @@ -[ - { - "id": "59760c9d.172d94", - "type": "http in", - "z": "d41b4dd3.ecd6a", - "name": "", - "url": "/hello-upload", - "method": "post", - "upload": true, - "swaggerDoc": "", - "x": 270, - "y": 1980, - "wires": [ - [ - "e30073f3.ad429" - ] - ] - }, - { - "id": "d0b3e8b4.2cfde8", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Post file to a flow", - "info": "The `HTTP In` node can listen for POST requests for file upload. Information for uploaded files can be accessed from `msg.req.files`.\n", - "x": 200, - "y": 1760, - "wires": [] - }, - { - "id": "a888a043.ebb72", - "type": "http in", - "z": "d41b4dd3.ecd6a", - "name": "", - "url": "/hello-upload", - "method": "get", - "upload": false, - "swaggerDoc": "", - "x": 270, - "y": 1880, - "wires": [ - [ - "14b950b.0ed5aaf" - ] - ] - }, - { - "id": "2ccb67c7.02c568", - "type": "http response", - "z": "d41b4dd3.ecd6a", - "name": "", - "statusCode": "", - "headers": {}, - "x": 590, - "y": 1880, - "wires": [] - }, - { - "id": "14b950b.0ed5aaf", - "type": "template", - "z": "d41b4dd3.ecd6a", - "name": "", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "mustache", - "template": "\n \n \n
    \n
    \n \n \n
    \n
    \n \n
    \n
    \n \n", - "output": "str", - "x": 440, - "y": 1880, - "wires": [ - [ - "2ccb67c7.02c568" - ] - ] - }, - { - "id": "54f2edb5.62fca4", - "type": "http response", - "z": "d41b4dd3.ecd6a", - "name": "", - "statusCode": "", - "headers": {}, - "x": 590, - "y": 1980, - "wires": [] - }, - { - "id": "e30073f3.ad429", - "type": "template", - "z": "d41b4dd3.ecd6a", - "name": "", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "mustache", - "template": "\n \n \n

    Hello {{req.files.0.originalname}}

    \n \n", - "output": "str", - "x": 440, - "y": 1980, - "wires": [ - [ - "54f2edb5.62fca4" - ] - ] - }, - { - "id": "6c3c1bf.48cc2e4", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Create HTTP page for file upload: open http://localhost:1880/hello-upload \\n from Web browser. Then upload a file.", - "info": "", - "x": 440, - "y": 1820, - "wires": [] - }, - { - "id": "c6242caa.8ec63", - "type": "comment", - "z": "d41b4dd3.ecd6a", - "name": "Serve POST request for file upload", - "info": "", - "x": 320, - "y": 1940, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json b/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json index 78c24760c..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/tcp/01 - Connect to TCP out server.json @@ -1,92 +0,0 @@ -[ - { - "id": "73874b9e.d985b4", - "type": "tcp in", - "z": "cfd9159c.6f73e8", - "name": "", - "server": "client", - "host": "localhost", - "port": "1881", - "datamode": "single", - "datatype": "utf8", - "newline": "", - "topic": "", - "base64": false, - "x": 230, - "y": 240, - "wires": [ - [ - "ce332113.d2c31" - ] - ] - }, - { - "id": "a4bd9948.dfeb58", - "type": "tcp out", - "z": "cfd9159c.6f73e8", - "host": "localhost", - "port": "1881", - "beserver": "server", - "base64": false, - "end": true, - "name": "", - "x": 390, - "y": 180, - "wires": [] - }, - { - "id": "9f80f9c7.1e3c98", - "type": "inject", - "z": "cfd9159c.6f73e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 210, - "y": 180, - "wires": [ - [ - "a4bd9948.dfeb58" - ] - ] - }, - { - "id": "ce332113.d2c31", - "type": "debug", - "z": "cfd9159c.6f73e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 410, - "y": 240, - "wires": [] - }, - { - "id": "711ed10d.1d765", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "Connect to TCP out server", - "info": "`TCP in` node can connect to network server using tcp protocol. `TCP out` node can serve a network server using tcp procol.\n", - "x": 190, - "y": 120, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json b/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json index 357cbb9ca..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/tcp/02 - Connect to TCP in server.json @@ -1,92 +0,0 @@ -[ - { - "id": "511f8208.c4c20c", - "type": "tcp in", - "z": "cfd9159c.6f73e8", - "name": "", - "server": "server", - "host": "localhost", - "port": "1882", - "datamode": "single", - "datatype": "utf8", - "newline": "", - "topic": "", - "base64": false, - "x": 230, - "y": 460, - "wires": [ - [ - "6b8be121.32be9" - ] - ] - }, - { - "id": "ec0bc4aa.b3c828", - "type": "tcp out", - "z": "cfd9159c.6f73e8", - "host": "localhost", - "port": "1882", - "beserver": "client", - "base64": false, - "end": true, - "name": "", - "x": 390, - "y": 400, - "wires": [] - }, - { - "id": "17cf7d56.9efb03", - "type": "inject", - "z": "cfd9159c.6f73e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 210, - "y": 400, - "wires": [ - [ - "ec0bc4aa.b3c828" - ] - ] - }, - { - "id": "6b8be121.32be9", - "type": "debug", - "z": "cfd9159c.6f73e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 410, - "y": 460, - "wires": [] - }, - { - "id": "76196665.7c23e8", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "Connect to TCP in server", - "info": "`TCP out` node can connect to network server using tcp protocol. `TCP in` node can serve a network server using tcp procol.\n", - "x": 190, - "y": 340, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json b/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json index b4a324261..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json +++ b/packages/node_modules/@node-red/nodes/examples/network/tcp/03 - Send reply to client of TCP connection.json @@ -1,174 +0,0 @@ -[ - { - "id": "d9a91d7e.05f0d", - "type": "tcp in", - "z": "cfd9159c.6f73e8", - "name": "", - "server": "server", - "host": "localhost", - "port": "1883", - "datamode": "stream", - "datatype": "utf8", - "newline": "¥n", - "topic": "", - "base64": false, - "x": 230, - "y": 740, - "wires": [ - [ - "3871d8af.25e208" - ] - ] - }, - { - "id": "9fa8f09d.7591b", - "type": "inject", - "z": "cfd9159c.6f73e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "\"Hello, World!¥n\"", - "payloadType": "jsonata", - "x": 190, - "y": 640, - "wires": [ - [ - "948a8410.ab0a08" - ] - ] - }, - { - "id": "33df08b.753e9f8", - "type": "debug", - "z": "cfd9159c.6f73e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 710, - "y": 640, - "wires": [] - }, - { - "id": "948a8410.ab0a08", - "type": "tcp request", - "z": "cfd9159c.6f73e8", - "server": "localhost", - "port": "1883", - "out": "char", - "splitc": "\\n", - "name": "", - "x": 350, - "y": 640, - "wires": [ - [ - "f6d6be6a.efb4c" - ] - ] - }, - { - "id": "fbd442d8.cb273", - "type": "tcp out", - "z": "cfd9159c.6f73e8", - "host": "", - "port": "", - "beserver": "reply", - "base64": false, - "end": false, - "name": "", - "x": 530, - "y": 740, - "wires": [] - }, - { - "id": "3871d8af.25e208", - "type": "change", - "z": "cfd9159c.6f73e8", - "name": "set result", - "rules": [ - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "\"Received: \" & payload & \"\b\\n\"", - "tot": "jsonata" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 400, - "y": 740, - "wires": [ - [ - "fbd442d8.cb273" - ] - ] - }, - { - "id": "f6d6be6a.efb4c", - "type": "function", - "z": "cfd9159c.6f73e8", - "name": "Buffer to String", - "func": "msg.payload = msg.payload.toString();\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 540, - "y": 640, - "wires": [ - [ - "33df08b.753e9f8" - ] - ] - }, - { - "id": "e1412987.91dcc8", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "Send reply to client of TCP connection", - "info": "Input message from `TCP in` node may be passed to `TCP out` node to return a reply to client.\n", - "x": 230, - "y": 580, - "wires": [] - }, - { - "id": "5f43905f.2425d", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "↓ Accept request", - "info": "", - "x": 220, - "y": 700, - "wires": [] - }, - { - "id": "a6f57329.c87c6", - "type": "comment", - "z": "cfd9159c.6f73e8", - "name": "↓ Reply result", - "info": "", - "x": 550, - "y": 700, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json b/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json index d53a05da8..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json +++ b/packages/node_modules/@node-red/nodes/examples/network/udp/01 - Transfer data using UDP protocol.json @@ -1,92 +0,0 @@ -[ - { - "id": "73279b5.5151664", - "type": "udp in", - "z": "92236e19.9e4cb", - "name": "", - "iface": "", - "port": "1881", - "ipv": "udp4", - "multicast": "false", - "group": "", - "datatype": "utf8", - "x": 220, - "y": 220, - "wires": [ - [ - "d98b60d3.7331e" - ] - ] - }, - { - "id": "fb19b98f.d5aa58", - "type": "udp out", - "z": "92236e19.9e4cb", - "name": "", - "addr": "localhost", - "iface": "", - "port": "1881", - "ipv": "udp4", - "outport": "", - "base64": false, - "multicast": "false", - "x": 410, - "y": 160, - "wires": [] - }, - { - "id": "33f18897.35d5b8", - "type": "inject", - "z": "92236e19.9e4cb", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 160, - "wires": [ - [ - "fb19b98f.d5aa58" - ] - ] - }, - { - "id": "d98b60d3.7331e", - "type": "debug", - "z": "92236e19.9e4cb", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 370, - "y": 220, - "wires": [] - }, - { - "id": "f86495aa.8a9848", - "type": "comment", - "z": "92236e19.9e4cb", - "name": "Transfer data using UDP protocol", - "info": "`UDP in` node can be used to receive data from `UDP out` node using UDP protocol.", - "x": 230, - "y": 100, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json b/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json index 5abc5c223..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/websocket/01 - Connect to websocket in server.json @@ -1,96 +0,0 @@ -[ - { - "id": "13410716.69c9d9", - "type": "websocket in", - "z": "a8360b47.074ec8", - "name": "", - "server": "89db22b6.9aa36", - "client": "", - "x": 280, - "y": 180, - "wires": [ - [ - "c2541f10.59544" - ] - ] - }, - { - "id": "c2541f10.59544", - "type": "debug", - "z": "a8360b47.074ec8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 430, - "y": 180, - "wires": [] - }, - { - "id": "6788839e.04576c", - "type": "inject", - "z": "a8360b47.074ec8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 290, - "y": 120, - "wires": [ - [ - "438c232a.06c2cc" - ] - ] - }, - { - "id": "438c232a.06c2cc", - "type": "websocket out", - "z": "a8360b47.074ec8", - "name": "", - "server": "", - "client": "63620788.bda128", - "x": 500, - "y": 120, - "wires": [] - }, - { - "id": "c88f97a9.4410f8", - "type": "comment", - "z": "a8360b47.074ec8", - "name": "Connect to websocket in server", - "info": "`websocket out` node can connect to web socket server. `websocket in` node can serve a web socket server.\n", - "x": 290, - "y": 80, - "wires": [] - }, - { - "id": "89db22b6.9aa36", - "type": "websocket-listener", - "path": "/ws1", - "wholemsg": "false" - }, - { - "id": "63620788.bda128", - "type": "websocket-client", - "path": "ws://localhost:1880/ws1", - "tls": "", - "wholemsg": "false" - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json b/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json index 667af1a95..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json +++ b/packages/node_modules/@node-red/nodes/examples/network/websocket/02 - Connect to websocket out server.json @@ -1,96 +0,0 @@ -[ - { - "id": "759c0b2b.8a0484", - "type": "websocket in", - "z": "a8360b47.074ec8", - "name": "", - "server": "", - "client": "1d80bd86.93f372", - "x": 340, - "y": 520, - "wires": [ - [ - "1f7a7454.cb65ec" - ] - ] - }, - { - "id": "1f7a7454.cb65ec", - "type": "debug", - "z": "a8360b47.074ec8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 550, - "y": 520, - "wires": [] - }, - { - "id": "aa2fe781.e92b28", - "type": "inject", - "z": "a8360b47.074ec8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Goodbye, World!", - "payloadType": "str", - "x": 300, - "y": 460, - "wires": [ - [ - "f8bdbc9b.d82dd" - ] - ] - }, - { - "id": "f8bdbc9b.d82dd", - "type": "websocket out", - "z": "a8360b47.074ec8", - "name": "", - "server": "40bd4295.3e4ecc", - "client": "", - "x": 460, - "y": 460, - "wires": [] - }, - { - "id": "c3fbc602.2e7f08", - "type": "comment", - "z": "a8360b47.074ec8", - "name": "Connect to websocket out server", - "info": "`websocket out` node can connect to web socket server. `websocket in` node can serve a web socket server.\n", - "x": 290, - "y": 420, - "wires": [] - }, - { - "id": "1d80bd86.93f372", - "type": "websocket-client", - "path": "ws://localhost:1880/ws2", - "tls": "", - "wholemsg": "false" - }, - { - "id": "40bd4295.3e4ecc", - "type": "websocket-listener", - "path": "/ws2", - "wholemsg": "false" - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json index ed75294b1..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/01 - Parse CSV with default column name as message sequence.json @@ -1,99 +0,0 @@ -[ - { - "id": "330f4888.cccb28", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 180, - "wires": [ - [ - "ed11f8d6.5e3c88" - ] - ] - }, - { - "id": "a0288b44.71d488", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": "", - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 180, - "wires": [ - [ - "369cbe42.4af9f2" - ] - ] - }, - { - "id": "ed11f8d6.5e3c88", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 180, - "wires": [ - [ - "a0288b44.71d488" - ] - ] - }, - { - "id": "369cbe42.4af9f2", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 180, - "wires": [] - }, - { - "id": "783cfaa6.52fbe4", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with default column name as messages", - "info": "CSV node can parse input CSV data.\nParsed CSV record can be sent as a message sequence.\nEach message payload points to an object with `col`*N* as a key and CSV value as a value.\n", - "x": 330, - "y": 120, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json index 85a03e486..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/02 - Parse CSV with default column name as array.json @@ -1,99 +0,0 @@ -[ - { - "id": "98c9d44d.4457b8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 360, - "wires": [ - [ - "65476517.3d760c" - ] - ] - }, - { - "id": "76df98f7.0dcd08", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": "", - "hdrout": "none", - "multi": "mult", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 360, - "wires": [ - [ - "557979e0.e6b588" - ] - ] - }, - { - "id": "65476517.3d760c", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 360, - "wires": [ - [ - "76df98f7.0dcd08" - ] - ] - }, - { - "id": "557979e0.e6b588", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 360, - "wires": [] - }, - { - "id": "187f4ab3.4c9ab5", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with default column name as array", - "info": "CSV node can send a single message with array of parsed CSV records.\nEach element of the array consists of objects with key-value pair.", - "x": 320, - "y": 300, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json index 327a25313..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/03 - Parse CSV with specified column name as message sequence.json @@ -1,99 +0,0 @@ -[ - { - "id": "1216e95b.1b1e87", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 560, - "wires": [ - [ - "e41ffbbc.de2ed8" - ] - ] - }, - { - "id": "286828bc.9233c8", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": "", - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "kind,price,origin", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 560, - "wires": [ - [ - "9d8218c.5550ee8" - ] - ] - }, - { - "id": "e41ffbbc.de2ed8", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 560, - "wires": [ - [ - "286828bc.9233c8" - ] - ] - }, - { - "id": "9d8218c.5550ee8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 560, - "wires": [] - }, - { - "id": "aaa1ee8f.21e2c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with specified column name as messages", - "info": "CSV node can specify column name of parsed objects in its settings panel.", - "x": 340, - "y": 500, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json index e287b57e7..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/04 - Parse CSV with column name in first row as message sequence.json @@ -1,99 +0,0 @@ -[ - { - "id": "24093558.0315aa", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 740, - "wires": [ - [ - "80abaee1.5fa7f" - ] - ] - }, - { - "id": "d4d2ca3f.1d9488", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": true, - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 740, - "wires": [ - [ - "b52791c3.08967" - ] - ] - }, - { - "id": "80abaee1.5fa7f", - "type": "template", - "z": "4b63452d.672afc", - "name": "CSV data", - "field": "payload", - "fieldType": "msg", - "format": "text", - "syntax": "mustache", - "template": "kind,price,origin\nApple,100,Canada\nOrange,120,USA\nBanana,80,Philippines", - "output": "str", - "x": 430, - "y": 740, - "wires": [ - [ - "d4d2ca3f.1d9488" - ] - ] - }, - { - "id": "b52791c3.08967", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 740, - "wires": [] - }, - { - "id": "85091361.85644", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Parse CSV with column name in first row as messages", - "info": "CSV node can use first row of input CSV text as a column name of each record object.\n", - "x": 340, - "y": 680, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json index a97656085..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/05 - Convert JavaScript object to CSV.json @@ -1,99 +0,0 @@ -[ - { - "id": "9e93169c.b763a8", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JavaScript object to CSV", - "info": "CSV node can convert a JavaScript object to CSV text.\nEach object contains key-value pair of specified properties.\n", - "x": 270, - "y": 860, - "wires": [] - }, - { - "id": "8ca41fee.3303d", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 920, - "wires": [ - [ - "c466905b.e8c61" - ] - ] - }, - { - "id": "65146d20.d78204", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 920, - "wires": [ - [ - "92e99e67.a37d8" - ] - ] - }, - { - "id": "c466905b.e8c61", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 430, - "y": 920, - "wires": [ - [ - "65146d20.d78204" - ] - ] - }, - { - "id": "92e99e67.a37d8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 920, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json index 0266dbe49..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/06 - Convert JavaScript object to CSV.json @@ -1,99 +0,0 @@ -[ - { - "id": "e89019c5.70ae78", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert array of JavaScript objects to CSV", - "info": "CSV node can convert an array of JavaScript objects to multi-line CSV text.", - "x": 300, - "y": 1020, - "wires": [] - }, - { - "id": "bd0d82ed.7b28", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 1080, - "wires": [ - [ - "1d857b8d.3a4014" - ] - ] - }, - { - "id": "66a37667.16ebd8", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "none", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 1080, - "wires": [ - [ - "859725fd.dc93d8" - ] - ] - }, - { - "id": "1d857b8d.3a4014", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]", - "output": "json", - "x": 430, - "y": 1080, - "wires": [ - [ - "66a37667.16ebd8" - ] - ] - }, - { - "id": "859725fd.dc93d8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 1080, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json index b1b337a06..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/07 - Convert array of JavaScript objects to CSV with column name header.json @@ -1,99 +0,0 @@ -[ - { - "id": "2ebdd51e.c5d17a", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert array of JavaScript objects to CSV with column name header", - "info": "CSV node can convert an array of JavaScript objects to multi-line CSV text with column name header at first line.", - "x": 390, - "y": 1200, - "wires": [] - }, - { - "id": "2b4d538d.ada07c", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 260, - "y": 1260, - "wires": [ - [ - "3e5c9e8.5065b62" - ] - ] - }, - { - "id": "db02c7be.0984e8", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "all", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 600, - "y": 1260, - "wires": [ - [ - "61f8b772.ddb1f8" - ] - ] - }, - { - "id": "3e5c9e8.5065b62", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]", - "output": "json", - "x": 430, - "y": 1260, - "wires": [ - [ - "db02c7be.0984e8" - ] - ] - }, - { - "id": "61f8b772.ddb1f8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 780, - "y": 1260, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json index 85901ba36..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/08 - Specify column names in input message.json @@ -1,126 +0,0 @@ -[ - { - "id": "b05816ab.7f2b08", - "type": "comment", - "z": "c6ffdacd.d887e8", - "name": "Specify column names in input message", - "info": "Column names can be specified by `columns` property of incoming message.\n", - "x": 240, - "y": 200, - "wires": [] - }, - { - "id": "39205b5c.690684", - "type": "inject", - "z": "c6ffdacd.d887e8", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 260, - "wires": [ - [ - "526b59ba.2fa068" - ] - ] - }, - { - "id": "b78a407e.2d083", - "type": "csv", - "z": "c6ffdacd.d887e8", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "all", - "multi": "one", - "ret": "\\n", - "temp": "", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 750, - "y": 260, - "wires": [ - [ - "8b7084dd.986f68" - ] - ] - }, - { - "id": "526b59ba.2fa068", - "type": "template", - "z": "c6ffdacd.d887e8", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]", - "output": "json", - "x": 370, - "y": 260, - "wires": [ - [ - "b204077a.227778" - ] - ] - }, - { - "id": "8b7084dd.986f68", - "type": "debug", - "z": "c6ffdacd.d887e8", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 930, - "y": 260, - "wires": [] - }, - { - "id": "b204077a.227778", - "type": "change", - "z": "c6ffdacd.d887e8", - "name": "", - "rules": [ - { - "t": "set", - "p": "columns", - "pt": "msg", - "to": "kind,price", - "tot": "str" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 570, - "y": 260, - "wires": [ - [ - "b78a407e.2d083" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json b/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json index 81d9b4730..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/csv/09 - Send column name when reset property set.json @@ -1,200 +0,0 @@ -[ - { - "id": "1ae28939.9f5fc7", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Send column name when reset property set", - "info": "CSV node can send column names at first or `reset` property exists in input message.", - "x": 310, - "y": 1540, - "wires": [] - }, - { - "id": "c16ad95b.4f9ac8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "Apple", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 250, - "y": 1600, - "wires": [ - [ - "7f7bfc72.aed104" - ] - ] - }, - { - "id": "870620b9.95343", - "type": "csv", - "z": "4b63452d.672afc", - "name": "", - "sep": ",", - "hdrin": false, - "hdrout": "once", - "multi": "one", - "ret": "\\n", - "temp": "kind,price", - "skip": "0", - "strings": true, - "include_empty_strings": "", - "include_null_values": "", - "x": 650, - "y": 1720, - "wires": [ - [ - "d960de42.619c7" - ] - ] - }, - { - "id": "7f7bfc72.aed104", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 470, - "y": 1600, - "wires": [ - [ - "870620b9.95343" - ] - ] - }, - { - "id": "d960de42.619c7", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 830, - "y": 1720, - "wires": [] - }, - { - "id": "6f8296e.f95ca68", - "type": "inject", - "z": "4b63452d.672afc", - "name": "Orange", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 250, - "y": 1660, - "wires": [ - [ - "c37d0dfa.ec1ab" - ] - ] - }, - { - "id": "c37d0dfa.ec1ab", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n}\n", - "output": "json", - "x": 470, - "y": 1660, - "wires": [ - [ - "870620b9.95343" - ] - ] - }, - { - "id": "35209fe2.16926", - "type": "inject", - "z": "4b63452d.672afc", - "name": "Banana & reset", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - }, - { - "p": "reset", - "v": "", - "vt": "date" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 280, - "y": 1720, - "wires": [ - [ - "afd4e6b3.624a28" - ] - ] - }, - { - "id": "afd4e6b3.624a28", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n}", - "output": "json", - "x": 470, - "y": 1720, - "wires": [ - [ - "870620b9.95343" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json b/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json index 14ef21214..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/html/01 - Extract array of HTML element by CSS selector.json @@ -1,94 +0,0 @@ -[ - { - "id": "8c5224a6.201b88", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 180, - "wires": [ - [ - "d6c67e51.0d709" - ] - ] - }, - { - "id": "d6c67e51.0d709", - "type": "template", - "z": "4b63452d.672afc", - "name": "HTML text", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "\n \n List of Fruits\n \n \n
      \n
    • Apple
    • \n
    • Orange
    • \n
    • Banana
    • \n
    \n \n\n", - "output": "str", - "x": 390, - "y": 180, - "wires": [ - [ - "599a1155.61a5c" - ] - ] - }, - { - "id": "b0d5cd89.338df", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Extract array of HTML element by CSS selector", - "info": "HTML node can be used to extract elements in HTML document as an array using CSS selector.", - "x": 280, - "y": 120, - "wires": [] - }, - { - "id": "599a1155.61a5c", - "type": "html", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": ".Item", - "ret": "html", - "as": "single", - "x": 550, - "y": 180, - "wires": [ - [ - "942b23d1.cce09" - ] - ] - }, - { - "id": "942b23d1.cce09", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 710, - "y": 180, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json b/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json index ccd3bc782..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/html/02 - Extract sequence of HTML element by CSS selector.json @@ -1,94 +0,0 @@ -[ - { - "id": "a44973e8.6319b", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 360, - "wires": [ - [ - "de1b012e.96ec3" - ] - ] - }, - { - "id": "de1b012e.96ec3", - "type": "template", - "z": "4b63452d.672afc", - "name": "HTML text", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "\n \n List of Fruits\n \n \n
      \n
    • Apple
    • \n
    • Orange
    • \n
    • Banana
    • \n
    \n \n\n", - "output": "str", - "x": 390, - "y": 360, - "wires": [ - [ - "cee70712.6f3538" - ] - ] - }, - { - "id": "99e32bc7.c8e508", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Extract sequence of HTML element by CSS selector", - "info": "HTML node can be used to extract elements in HTML document as a messege sequence using CSS selector.", - "x": 290, - "y": 300, - "wires": [] - }, - { - "id": "cee70712.6f3538", - "type": "html", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": ".Item", - "ret": "html", - "as": "multi", - "x": 550, - "y": 360, - "wires": [ - [ - "17f25482.d4b56b" - ] - ] - }, - { - "id": "17f25482.d4b56b", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 710, - "y": 360, - "wires": [] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json b/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json index ad2400ef6..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/html/03 - Extract array of HTML element by CSS selector specified in message.json @@ -1,121 +0,0 @@ -[ - { - "id": "653ce9aa.b6a1c8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 220, - "y": 560, - "wires": [ - [ - "52a16f7f.447d8" - ] - ] - }, - { - "id": "52a16f7f.447d8", - "type": "template", - "z": "4b63452d.672afc", - "name": "HTML text", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "plain", - "template": "\n \n List of Fruits\n \n \n
      \n
    • Apple
    • \n
    • Orange
    • \n
    • Banana
    • \n
    \n \n\n", - "output": "str", - "x": 390, - "y": 560, - "wires": [ - [ - "a52319c3.89b008" - ] - ] - }, - { - "id": "8bc35379.31d99", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Extract array of HTML element by CSS selector specified in message", - "info": "CSS selector for HTML node can be specified by `select` property of input message.", - "x": 350, - "y": 500, - "wires": [] - }, - { - "id": "9c49de8a.bad25", - "type": "html", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": "", - "ret": "html", - "as": "single", - "x": 730, - "y": 560, - "wires": [ - [ - "d4f4b987.278a68" - ] - ] - }, - { - "id": "d4f4b987.278a68", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 890, - "y": 560, - "wires": [] - }, - { - "id": "a52319c3.89b008", - "type": "change", - "z": "4b63452d.672afc", - "name": "", - "rules": [ - { - "t": "set", - "p": "select", - "pt": "msg", - "to": ".Item", - "tot": "str" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 560, - "y": 560, - "wires": [ - [ - "9c49de8a.bad25" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json b/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json index c95e71a7d..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/json/01 - Convert JSON string to JavaScript object.json @@ -1,92 +0,0 @@ -[ - { - "id": "9976e95d.2f8398", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 240, - "y": 180, - "wires": [ - [ - "d94fc083.49d87" - ] - ] - }, - { - "id": "6684abb1.8eb454", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JSON string to JS object", - "info": "JSON node can convert JSON string to JavaScript object.", - "x": 250, - "y": 120, - "wires": [] - }, - { - "id": "d94fc083.49d87", - "type": "template", - "z": "4b63452d.672afc", - "name": "JSON string", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "str", - "x": 410, - "y": 180, - "wires": [ - [ - "1a3dc54a.78598b" - ] - ] - }, - { - "id": "8950a55d.023988", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 730, - "y": 180, - "wires": [] - }, - { - "id": "1a3dc54a.78598b", - "type": "json", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "action": "", - "pretty": false, - "x": 570, - "y": 180, - "wires": [ - [ - "8950a55d.023988" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json b/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json index a10cc75b9..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/json/02 - Convert JavaScript object to JSON string.json @@ -1,92 +0,0 @@ -[ - { - "id": "cb13761f.56c328", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 240, - "y": 380, - "wires": [ - [ - "c607642a.78c3c8" - ] - ] - }, - { - "id": "180b1e22.0074e2", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JS object to JSON string", - "info": "JSON node can convert JavaScript object to JSON string.", - "x": 250, - "y": 320, - "wires": [] - }, - { - "id": "c607642a.78c3c8", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 400, - "y": 380, - "wires": [ - [ - "bf309844.fa12e8" - ] - ] - }, - { - "id": "5b6b130b.72a14c", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 730, - "y": 380, - "wires": [] - }, - { - "id": "bf309844.fa12e8", - "type": "json", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "action": "", - "pretty": false, - "x": 570, - "y": 380, - "wires": [ - [ - "5b6b130b.72a14c" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json b/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json index 6271f00c6..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/json/03 - Validate input JSON string.json @@ -1,160 +0,0 @@ -[ - { - "id": "2b18621b.e2670e", - "type": "inject", - "z": "4b63452d.672afc", - "name": "OK", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 230, - "y": 580, - "wires": [ - [ - "5986faee.aef954" - ] - ] - }, - { - "id": "59acf99.9a92308", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Validate input JSON string", - "info": "JSON node can validate input JSON string using [JSON schema](https://json-schema.org/) when converting to JavaScript object.", - "x": 230, - "y": 520, - "wires": [] - }, - { - "id": "5986faee.aef954", - "type": "template", - "z": "4b63452d.672afc", - "name": "JSON string", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "str", - "x": 410, - "y": 580, - "wires": [ - [ - "f8a67c6d.4f1f1" - ] - ] - }, - { - "id": "ca27c92c.ad7cb8", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 910, - "y": 580, - "wires": [] - }, - { - "id": "2fad9978.ea1916", - "type": "json", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "action": "", - "pretty": false, - "x": 750, - "y": 580, - "wires": [ - [ - "ca27c92c.ad7cb8" - ] - ] - }, - { - "id": "f8a67c6d.4f1f1", - "type": "template", - "z": "4b63452d.672afc", - "name": "Schema", - "field": "schema", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"type\": \"object\",\n \"properties\": {\n \"kind\": {\n \"type\": \"string\"\n },\n \"price\": {\n \"type\": \"number\"\n },\n \"origin\": {\n \"type\": \"string\"\n }\n }\n}", - "output": "json", - "x": 590, - "y": 580, - "wires": [ - [ - "2fad9978.ea1916" - ] - ] - }, - { - "id": "8337e847.ac18d8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "NG", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 230, - "y": 660, - "wires": [ - [ - "fa14d8bf.1ac938" - ] - ] - }, - { - "id": "fa14d8bf.1ac938", - "type": "template", - "z": "4b63452d.672afc", - "name": "JSON string", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": \"100\",\n \"origin\": \"Canada\"\n}", - "output": "str", - "x": 410, - "y": 660, - "wires": [ - [ - "f8a67c6d.4f1f1" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json b/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json index 3a97c8498..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/xml/01 - Convert JavaScript object to XML.json @@ -1,92 +0,0 @@ -[ - { - "id": "82f1bd0b.43474", - "type": "xml", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "attr": "", - "chr": "", - "x": 530, - "y": 180, - "wires": [ - [ - "1cd4ad02.9a5423" - ] - ] - }, - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 180, - "wires": [ - [ - "cdd1c154.3a655" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JavaScript object to XML", - "info": "XML node can convert JavaScript object to XML string.", - "x": 240, - "y": 120, - "wires": [] - }, - { - "id": "1cd4ad02.9a5423", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 690, - "y": 180, - "wires": [] - }, - { - "id": "cdd1c154.3a655", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}", - "output": "json", - "x": 360, - "y": 180, - "wires": [ - [ - "82f1bd0b.43474" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json b/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json index 5e46428ef..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/xml/02 - Convert XML to JavaScript object.json @@ -1,92 +0,0 @@ -[ - { - "id": "93e423a9.a407d", - "type": "xml", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "attr": "", - "chr": "", - "x": 530, - "y": 360, - "wires": [ - [ - "2d0dde7e.a50082" - ] - ] - }, - { - "id": "ba1dab90.8d1da8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 360, - "wires": [ - [ - "16617f26.14ced1" - ] - ] - }, - { - "id": "a9f97b00.57d658", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert XML to JavaScript object", - "info": "XML node can convert XML string to JavaScript object.", - "x": 240, - "y": 300, - "wires": [] - }, - { - "id": "2d0dde7e.a50082", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 690, - "y": 360, - "wires": [] - }, - { - "id": "16617f26.14ced1", - "type": "template", - "z": "4b63452d.672afc", - "name": "XML string", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "plain", - "template": "\n\n Apple\n 100\n Canada\n", - "output": "str", - "x": 370, - "y": 360, - "wires": [ - [ - "93e423a9.a407d" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json b/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json index 7a3c39f7e..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/xml/03 - Control conversion using options property.json @@ -1,119 +0,0 @@ -[ - { - "id": "581bd648.636628", - "type": "xml", - "z": "4b63452d.672afc", - "name": "", - "property": "payload", - "attr": "", - "chr": "", - "x": 710, - "y": 540, - "wires": [ - [ - "b74237dc.1e5028" - ] - ] - }, - { - "id": "d0899f9b.f1ac6", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 540, - "wires": [ - [ - "f04ffb9a.68edb8" - ] - ] - }, - { - "id": "8a214c05.dc61f", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Control conversion using options property", - "info": "XML node can control conversion by setting `options` property (defined by [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options)) in input message.", - "x": 260, - "y": 480, - "wires": [] - }, - { - "id": "b74237dc.1e5028", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 870, - "y": 540, - "wires": [] - }, - { - "id": "f04ffb9a.68edb8", - "type": "template", - "z": "4b63452d.672afc", - "name": "XML string", - "field": "payload", - "fieldType": "msg", - "format": "html", - "syntax": "plain", - "template": "\n\n Apple\n 100\n Canada\n", - "output": "str", - "x": 370, - "y": 540, - "wires": [ - [ - "fedf79.5889c088" - ] - ] - }, - { - "id": "fedf79.5889c088", - "type": "change", - "z": "4b63452d.672afc", - "name": "set options", - "rules": [ - { - "t": "set", - "p": "options", - "pt": "msg", - "to": "{\"explicitArray\":false}", - "tot": "json" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 550, - "y": 540, - "wires": [ - [ - "581bd648.636628" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json b/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json index d751a8731..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/yaml/01 - Convert JavaScript object to YAML.json @@ -1,90 +0,0 @@ -[ - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 180, - "wires": [ - [ - "cdd1c154.3a655" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert JavaScript object to YAML", - "info": "YAML node can convert JavaScript object to YAML string.", - "x": 240, - "y": 120, - "wires": [] - }, - { - "id": "1cd4ad02.9a5423", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 670, - "y": 180, - "wires": [] - }, - { - "id": "cdd1c154.3a655", - "type": "template", - "z": "4b63452d.672afc", - "name": "JS object", - "field": "payload", - "fieldType": "msg", - "format": "json", - "syntax": "plain", - "template": "{\n \"fruits\" : {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n }\n}", - "output": "json", - "x": 360, - "y": 180, - "wires": [ - [ - "aaf0100b.16628" - ] - ] - }, - { - "id": "aaf0100b.16628", - "type": "yaml", - "z": "4b63452d.672afc", - "property": "payload", - "name": "", - "x": 510, - "y": 180, - "wires": [ - [ - "1cd4ad02.9a5423" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json b/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json index d80e0d144..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json +++ b/packages/node_modules/@node-red/nodes/examples/parser/yaml/02 - Convert YAML to JavaScript object.json @@ -1,90 +0,0 @@ -[ - { - "id": "ba1dab90.8d1da8", - "type": "inject", - "z": "4b63452d.672afc", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "", - "payloadType": "date", - "x": 200, - "y": 360, - "wires": [ - [ - "16617f26.14ced1" - ] - ] - }, - { - "id": "a9f97b00.57d658", - "type": "comment", - "z": "4b63452d.672afc", - "name": "Convert YAML to JavaScript object", - "info": "YAML node can convert YAML string to JavaScript object.", - "x": 240, - "y": 300, - "wires": [] - }, - { - "id": "2d0dde7e.a50082", - "type": "debug", - "z": "4b63452d.672afc", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 690, - "y": 360, - "wires": [] - }, - { - "id": "16617f26.14ced1", - "type": "template", - "z": "4b63452d.672afc", - "name": "YAML string", - "field": "payload", - "fieldType": "msg", - "format": "yaml", - "syntax": "plain", - "template": "fruits:\n kind: Apple\n price: 100\n origin: Canada", - "output": "str", - "x": 370, - "y": 360, - "wires": [ - [ - "e2e4f862.f9d7d8" - ] - ] - }, - { - "id": "e2e4f862.f9d7d8", - "type": "yaml", - "z": "4b63452d.672afc", - "property": "payload", - "name": "", - "x": 530, - "y": 360, - "wires": [ - [ - "2d0dde7e.a50082" - ] - ] - } -] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json b/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json index 74bb99eb4..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json +++ b/packages/node_modules/@node-red/nodes/examples/sequence/sort/01 - Sort array payload.json @@ -1 +0,0 @@ -[{"id":"6451c8bb.b52278","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":160,"wires":[["cb34307c.ac1dd"]]},{"id":"638546c.38f1fb8","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":160,"wires":[["db5d90ac.bbb3f"]]},{"id":"3ec02cae.012ce4","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as string in ascending order","info":"","x":280,"y":100,"wires":[]},{"id":"db5d90ac.bbb3f","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":160,"wires":[["6451c8bb.b52278"]]},{"id":"cb34307c.ac1dd","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":160,"wires":[]},{"id":"a0de8ca4.6601f","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":480,"wires":[["ca74a53e.90fc08"]]},{"id":"23d253dc.5e990c","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":480,"wires":[["4ccd5aed.ca24e4"]]},{"id":"19946d36.185313","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as string","info":"","x":220,"y":420,"wires":[]},{"id":"4ccd5aed.ca24e4","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":480,"wires":[["a0de8ca4.6601f"]]},{"id":"ca74a53e.90fc08","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":480,"wires":[]},{"id":"d4b49c22.32685","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":true,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":640,"wires":[["45738f07.16416"]]},{"id":"87ce9955.924868","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":640,"wires":[["30b81283.f0772e"]]},{"id":"f6e624df.703a88","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as number","info":"","x":220,"y":580,"wires":[]},{"id":"30b81283.f0772e","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":640,"wires":[["d4b49c22.32685"]]},{"id":"45738f07.16416","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":640,"wires":[]},{"id":"b91c19b1.b66b18","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"price","msgKeyType":"jsonata","seqKey":"payload","seqKeyType":"msg","x":510,"y":800,"wires":[["32dd80a1.226e4"]]},{"id":"adb0daa2.d85a48","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":800,"wires":[["b3ee1b9e.dbf388"]]},{"id":"d1190f8b.9a74b","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array of objects payload using simple JSONata expression","info":"","x":330,"y":740,"wires":[]},{"id":"b3ee1b9e.dbf388","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 80\n },\n {\n \"name\": \"banana\",\n \"price\": 250\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 150\n },\n {\n \"name\": \"kiwi\",\n \"price\": 320\n }\n]","output":"json","x":370,"y":800,"wires":[["b91c19b1.b66b18"]]},{"id":"32dd80a1.226e4","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":800,"wires":[]},{"id":"735ec14e.4ed55","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"descending","as_num":false,"target":"payload","targetType":"msg","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":510,"y":320,"wires":[["e8dc4ae5.f08598"]]},{"id":"c8ce4e74.9db68","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":320,"wires":[["ea0384a6.1346b8"]]},{"id":"70333397.d78f6c","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array payload as string in descending order","info":"","x":280,"y":260,"wires":[]},{"id":"ea0384a6.1346b8","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":320,"wires":[["735ec14e.4ed55"]]},{"id":"e8dc4ae5.f08598","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":320,"wires":[]},{"id":"e4025b79.fccdd8","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort using object property as sorting key","info":"","x":620,"y":840,"wires":[]},{"id":"2eaa93da.543cac","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of number","info":"","x":600,"y":680,"wires":[]},{"id":"24da49c5.785676","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of string","info":"","x":590,"y":520,"wires":[]},{"id":"dc856174.29eb5","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of string in descending order","info":"","x":650,"y":360,"wires":[]},{"id":"4bab755b.a073dc","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort payload as array of string in ascending order","info":"","x":650,"y":200,"wires":[]},{"id":"e947beb9.2ec5e","type":"sort","z":"9f9f8c22.7e6b2","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"$substring(\"0000\" & $string(price), -4) & name","msgKeyType":"jsonata","seqKey":"payload","seqKeyType":"msg","x":510,"y":960,"wires":[["6a4af14a.cafc4"]]},{"id":"d19d7ff5.135f2","type":"inject","z":"9f9f8c22.7e6b2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":960,"wires":[["5eb37a3e.3c2184"]]},{"id":"91e34f52.e1413","type":"comment","z":"9f9f8c22.7e6b2","name":"Sort array of objects payload using complex JSONata expression","info":"","x":330,"y":900,"wires":[]},{"id":"5eb37a3e.3c2184","type":"template","z":"9f9f8c22.7e6b2","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 100\n },\n {\n \"name\": \"banana\",\n \"price\": 200\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 200\n },\n {\n \"name\": \"kiwi\",\n \"price\": 200\n }\n]","output":"json","x":370,"y":960,"wires":[["e947beb9.2ec5e"]]},{"id":"6a4af14a.cafc4","type":"debug","z":"9f9f8c22.7e6b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":960,"wires":[]},{"id":"1b068260.e3200e","type":"comment","z":"9f9f8c22.7e6b2","name":"↑ sort using object two property (price and name) as sorting keys","info":"","x":690,"y":1000,"wires":[]},{"id":"7ced03b9.75dd8c","type":"comment","z":"9f9f8c22.7e6b2","name":"Example: Sort Array Payload","info":"Sort node can be used to message payload that points to a JavaScript array. It can specify sort order and sort key. Sort key can be payload value or JSONata expression. If JSONata expression is used, the expression is applied to `payload` value.\n","x":180,"y":40,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json b/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json index abcd3cfdb..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json +++ b/packages/node_modules/@node-red/nodes/examples/sequence/sort/02 - Sort message sequence.json @@ -1 +0,0 @@ -[{"id":"60b477b9.188ce8","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":180,"wires":[["38fcd115.f147ae"]]},{"id":"35b577bf.827978","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":180,"wires":[["5a8dfb10.5ed8b4"]]},{"id":"e8e4517b.e2139","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as string in ascending order","info":"Sort node can be used to message sequence that contains `parts` property. If JSONata expression is used, the expression is applied to input message.","x":280,"y":100,"wires":[]},{"id":"5a8dfb10.5ed8b4","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":180,"wires":[["a509486a.9c32a8"]]},{"id":"38fcd115.f147ae","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":180,"wires":[]},{"id":"eca99b1d.c456c8","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort message sequence in ascending order","info":"","x":770,"y":220,"wires":[]},{"id":"a509486a.9c32a8","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":180,"wires":[["60b477b9.188ce8"]]},{"id":"56294459.e2431c","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":140,"wires":[]},{"id":"f8ab27f2.b81d78","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":520,"wires":[["75369287.5bb00c"]]},{"id":"df79c07e.d9dce","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":520,"wires":[["8e1e5b1e.a979e8"]]},{"id":"de7c57f3.7ecae8","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as string","info":"","x":220,"y":440,"wires":[]},{"id":"8e1e5b1e.a979e8","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":520,"wires":[["4392ddd6.e30404"]]},{"id":"75369287.5bb00c","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":520,"wires":[]},{"id":"f9100b9d.065088","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":true,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":700,"wires":[["53127c8c.cb4b64"]]},{"id":"c010d098.ac0e8","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":700,"wires":[["902c97aa.8b2958"]]},{"id":"8531cf78.dcdfc","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as number","info":"","x":220,"y":620,"wires":[]},{"id":"902c97aa.8b2958","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"1024\",\n \"86\",\n \"256\",\n \"100\",\n \"9\"\n]","output":"json","x":370,"y":700,"wires":[["99461434.772718"]]},{"id":"53127c8c.cb4b64","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":700,"wires":[]},{"id":"ea246a52.4b9d88","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"price","msgKeyType":"jsonata","seqKey":"payload.price","seqKeyType":"jsonata","x":650,"y":880,"wires":[["2d548e4c.9adbd2"]]},{"id":"d69bd6df.849d68","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":880,"wires":[["e6ef0fbc.6fc22"]]},{"id":"ee5f9da5.d21ee","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array of objects payload using simple JSONata expression","info":"","x":330,"y":800,"wires":[]},{"id":"e6ef0fbc.6fc22","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 80\n },\n {\n \"name\": \"banana\",\n \"price\": 250\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 150\n },\n {\n \"name\": \"kiwi\",\n \"price\": 320\n }\n]","output":"json","x":370,"y":880,"wires":[["fcfd304c.562c9"]]},{"id":"2d548e4c.9adbd2","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":880,"wires":[]},{"id":"4ec23d0b.a4f014","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"descending","as_num":false,"target":"","targetType":"seq","msgKey":"","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":650,"y":340,"wires":[["b1f8d641.a96a78"]]},{"id":"4aa6a253.159aec","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":340,"wires":[["f28101b7.f4b21"]]},{"id":"63d42ecf.fd933","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array payload as string in descending order","info":"","x":280,"y":260,"wires":[]},{"id":"f28101b7.f4b21","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n \"orange\",\n \"banana\",\n \"apple\",\n \"pear\",\n \"kiwi\"\n]","output":"json","x":370,"y":340,"wires":[["685d7c1b.4f92b4"]]},{"id":"b1f8d641.a96a78","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":340,"wires":[]},{"id":"c112dd05.1543e","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort using object property as sorting key","info":"","x":760,"y":920,"wires":[]},{"id":"5c5ff25a.0f22bc","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort payload as array of number","info":"","x":740,"y":740,"wires":[]},{"id":"d0270e9b.04b7d","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort payload as array of string","info":"","x":730,"y":560,"wires":[]},{"id":"6ae587cd.cf9478","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort payload as array of string in descending order","info":"","x":790,"y":380,"wires":[]},{"id":"6fba3dab.8218c4","type":"sort","z":"62d2b20a.1a87bc","name":"","order":"ascending","as_num":false,"target":"","targetType":"seq","msgKey":"$substring(\"0000\" & $string(price), -4) & name","msgKeyType":"jsonata","seqKey":"$substring(\"0000\" & $string(payload.price), -4) & payload.name","seqKeyType":"jsonata","x":650,"y":1060,"wires":[["95f7f1b0.39fac"]]},{"id":"892b1f19.60247","type":"inject","z":"62d2b20a.1a87bc","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":1060,"wires":[["80ca6396.f55bb"]]},{"id":"c2da4a48.d35cd8","type":"comment","z":"62d2b20a.1a87bc","name":"Sort array of objects payload using complex JSONata expression","info":"","x":330,"y":980,"wires":[]},{"id":"80ca6396.f55bb","type":"template","z":"62d2b20a.1a87bc","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"[\n {\n \"name\": \"orange\", \n \"price\": 100\n },\n {\n \"name\": \"banana\",\n \"price\": 200\n },\n {\n \"name\": \"apple\",\n \"price\": 100\n },\n {\n \"name\": \"pear\",\n \"price\": 200\n },\n {\n \"name\": \"kiwi\",\n \"price\": 200\n }\n]","output":"json","x":370,"y":1060,"wires":[["4cda7453.75eb1c"]]},{"id":"95f7f1b0.39fac","type":"debug","z":"62d2b20a.1a87bc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":1060,"wires":[]},{"id":"24472c63.d12294","type":"comment","z":"62d2b20a.1a87bc","name":"↑ sort using object two property (price and name) as sorting keys","info":"","x":830,"y":1100,"wires":[]},{"id":"685d7c1b.4f92b4","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":340,"wires":[["4ec23d0b.a4f014"]]},{"id":"4392ddd6.e30404","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":520,"wires":[["f8ab27f2.b81d78"]]},{"id":"99461434.772718","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":700,"wires":[["f9100b9d.065088"]]},{"id":"fcfd304c.562c9","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":880,"wires":[["ea246a52.4b9d88"]]},{"id":"4cda7453.75eb1c","type":"split","z":"62d2b20a.1a87bc","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":true,"addname":"","x":510,"y":1060,"wires":[["6fba3dab.8218c4"]]},{"id":"1b2b8efc.b5aa51","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":300,"wires":[]},{"id":"972a454c.99b5e8","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":480,"wires":[]},{"id":"39123b6a.ee5bb4","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":660,"wires":[]},{"id":"7809a5ac.eb0f8c","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":840,"wires":[]},{"id":"f62c6678.d4da08","type":"comment","z":"62d2b20a.1a87bc","name":"↓ split array payload to message sequence","info":"","x":620,"y":1020,"wires":[]},{"id":"2438c53f.71a0da","type":"comment","z":"62d2b20a.1a87bc","name":"Example: Sort Message Sequence","info":"Sort node can be used to message sequence that contains `parts` property. If JSONata expression is used, the expression is applied to input message.","x":200,"y":40,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json b/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json index 3fb14f0c3..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json +++ b/packages/node_modules/@node-red/nodes/examples/sequence/split/01 - Split message payload.json @@ -1 +0,0 @@ -[{"id":"f94ffc33.76f83","type":"comment","z":"e5679299.d9792","name":"Example: Split Message Payload","info":"Split node can be used to split message payload into multiple messages.","x":190,"y":60,"wires":[]},{"id":"657bb57c.a3f98c","type":"split","z":"e5679299.d9792","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":510,"y":160,"wires":[["14228ff.ae24f7"]]},{"id":"2afece55.b87de2","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":160,"wires":[["cfca3863.d961d8"]]},{"id":"cfca3863.d961d8","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"Apple\nOrange\nBanana","output":"str","x":370,"y":160,"wires":[["657bb57c.a3f98c"]]},{"id":"14228ff.ae24f7","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":660,"y":160,"wires":[]},{"id":"9a3c9494.b5d178","type":"comment","z":"e5679299.d9792","name":"Split input text by specified string","info":"","x":230,"y":120,"wires":[]},{"id":"38c873c.5ae718c","type":"comment","z":"e5679299.d9792","name":"↑ split by newline (\\\\n)","info":"","x":560,"y":200,"wires":[]},{"id":"bdfa12b9.3fbbc","type":"split","z":"e5679299.d9792","name":"","splt":"4","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":510,"y":280,"wires":[["debf23bb.c0245"]]},{"id":"7c0948db.e35d38","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":280,"wires":[["7f791b7d.94cad4"]]},{"id":"7f791b7d.94cad4","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"Apple\nOrange\nBanana","output":"str","x":370,"y":280,"wires":[["bdfa12b9.3fbbc"]]},{"id":"debf23bb.c0245","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":660,"y":280,"wires":[]},{"id":"f05e98a3.4182c8","type":"comment","z":"e5679299.d9792","name":"Split input text by specified number of characters","info":"","x":280,"y":240,"wires":[]},{"id":"86b52b51.2258d8","type":"comment","z":"e5679299.d9792","name":"↑ split by four characters","info":"","x":570,"y":320,"wires":[]},{"id":"71d7c0e0.c0316","type":"split","z":"e5679299.d9792","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":510,"y":420,"wires":[["bee5b6a2.a955a8"]]},{"id":"1cdc2df9.bebdd2","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":420,"wires":[["f30df13c.19475"]]},{"id":"f30df13c.19475","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"[ \n \"Apple\",\n \"Orange\",\n \"Banana\"\n]","output":"json","x":370,"y":420,"wires":[["71d7c0e0.c0316"]]},{"id":"bee5b6a2.a955a8","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":660,"y":420,"wires":[]},{"id":"b2c731fe.abda4","type":"comment","z":"e5679299.d9792","name":"Split input array","info":"","x":180,"y":380,"wires":[]},{"id":"1f557227.d0910e","type":"comment","z":"e5679299.d9792","name":"↑ split array","info":"","x":530,"y":460,"wires":[]},{"id":"c0d43ff4.291d8","type":"split","z":"e5679299.d9792","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"topic","x":510,"y":540,"wires":[["fc9fe458.50fd18"]]},{"id":"6d52ce8a.0c715","type":"inject","z":"e5679299.d9792","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":540,"wires":[["bdeb8c21.1c6b7"]]},{"id":"bdeb8c21.1c6b7","type":"template","z":"e5679299.d9792","name":"data","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"{ \n \"Apple\": 80,\n \"Orange\": 100,\n \"Banana\": 50\n}","output":"json","x":370,"y":540,"wires":[["c0d43ff4.291d8"]]},{"id":"fc9fe458.50fd18","type":"debug","z":"e5679299.d9792","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":640,"y":540,"wires":[]},{"id":"48956e14.79b86","type":"comment","z":"e5679299.d9792","name":"Split object to key/value pairs","info":"","x":220,"y":500,"wires":[]},{"id":"d528c2c2.6efc7","type":"comment","z":"e5679299.d9792","name":"↑ split object","info":"","x":530,"y":580,"wires":[]}] \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json b/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json index 873e99b89..e69de29bb 100644 --- a/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json +++ b/packages/node_modules/@node-red/nodes/examples/storage/watch/01 - Watch change of a file.json @@ -1,108 +0,0 @@ -[ - { - "id": "84222b92.d65d18", - "type": "inject", - "z": "a7ac8a68.0f7218", - "name": "", - "props": [ - { - "p": "payload" - }, - { - "p": "topic", - "vt": "str" - } - ], - "repeat": "", - "crontab": "", - "once": false, - "onceDelay": 0.1, - "topic": "", - "payload": "Hello, World!", - "payloadType": "str", - "x": 230, - "y": 160, - "wires": [ - [ - "b4b9f603.739598" - ] - ] - }, - { - "id": "7b014430.dfd94c", - "type": "comment", - "z": "a7ac8a68.0f7218", - "name": "Watch changes of a file", - "info": "Watch node can watch and report changes of a file.", - "x": 200, - "y": 80, - "wires": [] - }, - { - "id": "b4b9f603.739598", - "type": "file", - "z": "a7ac8a68.0f7218", - "name": "", - "filename": "/tmp/hello.txt", - "appendNewline": true, - "createDir": false, - "overwriteFile": "true", - "encoding": "none", - "x": 420, - "y": 160, - "wires": [ - [] - ] - }, - { - "id": "672d3693.3cabd8", - "type": "comment", - "z": "a7ac8a68.0f7218", - "name": "↓write to /tmp/hello.txt", - "info": "", - "x": 440, - "y": 120, - "wires": [] - }, - { - "id": "15f1f5aa.506ffa", - "type": "watch", - "z": "a7ac8a68.0f7218", - "name": "", - "files": "/tmp/hello.txt", - "recursive": "", - "x": 410, - "y": 200, - "wires": [ - [ - "a91562b9.ca805" - ] - ] - }, - { - "id": "a91562b9.ca805", - "type": "debug", - "z": "a7ac8a68.0f7218", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "false", - "statusVal": "", - "statusType": "auto", - "x": 610, - "y": 200, - "wires": [] - }, - { - "id": "2ab4eba8.267d64", - "type": "comment", - "z": "a7ac8a68.0f7218", - "name": "↑watch changes of /tmp/hello.txt", - "info": "", - "x": 470, - "y": 240, - "wires": [] - } -] \ No newline at end of file diff --git a/test/editor/editor_helper.js b/test/editor/editor_helper.js deleted file mode 100644 index 73177c2ca..000000000 --- a/test/editor/editor_helper.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require("when"); -var http = require('http'); -var express = require("express"); -var fs = require('fs-extra'); -var path = require('path'); -var app = express(); - -var RED = require("nr-test-utils").require("node-red/lib/red.js"); - -var utilPage = require("./pageobjects/util/util_page"); - -var server; -var homeDir = './test/resources/home'; -var address = '127.0.0.1'; -var listenPort = 0; // use ephemeral port -var url; -/* - * Set false when you need a flow to reproduce the failed test case. - * The flow file is under "homeDir" defined above. - */ -var isDeleteFlow = true; - -function getFlowFilename() { - var orig = Error.prepareStackTrace; - var err = new Error(); - Error.prepareStackTrace = function (err, stack) { - return stack; - }; - // Two level higher caller is the actual caller (e.g. a caller of startServer). - var filepath = err.stack[2].getFileName(); - var filename = path.basename(filepath, ".js"); - Error.prepareStackTrace = orig; - - var flowFile = 'flows_' + filename + '.json'; - return flowFile; -} - -function cleanup(flowFile) { - var credentialFile = flowFile.replace(/\.json$/, '') + '_cred.json'; - deleteFile(homeDir + "/" + flowFile); - deleteFile(homeDir + "/." + flowFile + ".backup"); - deleteFile(homeDir + "/" + credentialFile); - deleteFile(homeDir + "/." + credentialFile + ".backup"); - deleteFile(homeDir + "/package.json"); - deleteFile(homeDir + "/lib/flows"); - deleteFile(homeDir + "/lib"); -} - -function deleteFile(flowFile) { - try { - fs.statSync(flowFile); - if (isDeleteFlow) { - fs.unlinkSync(flowFile); - } - } catch (e) {} -} - -module.exports = { - startServer: function() { - try{ - utilPage.init(); - - // Name a flow file including caller filename so that multiple Node-RED servers can run simultaneously. - // Call this method here because retrieving the caller filename by call stack. - var flowFilename = getFlowFilename(); - browser.windowHandleMaximize(); - browser.call(function () { - return new Promise(function(resolve, reject) { - cleanup(flowFilename); - server = http.createServer(app); - var settings = { - httpAdminRoot: "/", - httpNodeRoot: "/api", - userDir: homeDir, - flowFile: flowFilename, - functionGlobalContext: { }, // enables global context - SKIP_BUILD_CHECK: true, - logging: {console: {level:'off'}} - }; - RED.init(server, settings); - app.use(settings.httpAdminRoot,RED.httpAdmin); - app.use(settings.httpNodeRoot,RED.httpNode); - server.listen(listenPort, address); - server.on('listening', function() { - var port = server.address().port; - url = 'http://' + address + ':' + port; - }); - RED.start().then(function() { - resolve(); - }); - }); - }); - browser.url(url); - browser.waitForExist(".red-ui-palette-node[data-palette-type='inject']"); - } catch (err) { - console.log(err); - throw err; - } - }, - - stopServer: function(done) { - try { - // Call this method here because retrieving the caller filename by call stack. - var flowFilename = getFlowFilename(); - browser.call(function () { - browser.close(); // need to call this inside browser.call(). - return when.promise(function(resolve, reject) { - if (server) { - RED.stop().then(function() { - server.close(function() { - cleanup(flowFilename); - resolve(); - }); - }); - } else { - cleanup(flowFilename); - resolve(); - } - }); - }); - } catch (err) { - console.log(err); - throw err; - } - }, - - url: function() { - return url; - }, - - red: function() { - return RED; - }, -}; diff --git a/test/editor/pageobjects/editor/debugTab_page.js b/test/editor/pageobjects/editor/debugTab_page.js deleted file mode 100644 index 5476a28d6..000000000 --- a/test/editor/pageobjects/editor/debugTab_page.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -function open(retainMessage) { - browser.clickWithWait('#red-ui-tab-debug-link-button'); - - if (!retainMessage) { - // Clear old messages - browser.clickWithWait('//a[@id="red-ui-sidebar-debug-clear"]'); - } -} - -function getMessage(index) { - index = index ? index : 1; - var debugMessagePath = '//div[@class="red-ui-debug-content red-ui-debug-content-list"]/div[contains(@class,"red-ui-debug-msg")][' + index + ']//span[contains(@class, "red-ui-debug-msg-type")]'; - return browser.getTextWithWait(debugMessagePath); -} - -function clearMessage() { - browser.clickWithWait('//a[@id="red-ui-sidebar-debug-clear"]'); -} - -module.exports = { - open: open, - getMessage: getMessage, - clearMessage: clearMessage, -}; diff --git a/test/editor/pageobjects/editor/palette_page.js b/test/editor/pageobjects/editor/palette_page.js deleted file mode 100644 index 3b484a58c..000000000 --- a/test/editor/pageobjects/editor/palette_page.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var idMap = { - // common - "inject": ".red-ui-palette-node[data-palette-type='inject']", - "debug": ".red-ui-palette-node[data-palette-type='debug']", - "complete": ".red-ui-palette-node[data-palette-type='complete']", - "catch": ".red-ui-palette-node[data-palette-type='catch']", - "status": ".red-ui-palette-node[data-palette-type='status']", - "comment": ".red-ui-palette-node[data-palette-type='comment']", - // function - "function": ".red-ui-palette-node[data-palette-type='function']", - "switch": ".red-ui-palette-node[data-palette-type='switch']", - "change": ".red-ui-palette-node[data-palette-type='change']", - "range": ".red-ui-palette-node[data-palette-type='range']", - "template": ".red-ui-palette-node[data-palette-type='template']", - "delay": ".red-ui-palette-node[data-palette-type='delay']", - "trigger": ".red-ui-palette-node[data-palette-type='trigger']", - "exec": ".red-ui-palette-node[data-palette-type='exec']", - // network - "mqttIn": ".red-ui-palette-node[data-palette-type='mqtt in']", - "mqttOut": ".red-ui-palette-node[data-palette-type='mqtt out']", - "httpIn": ".red-ui-palette-node[data-palette-type='http in']", - "httpResponse": ".red-ui-palette-node[data-palette-type='http response']", - "httpRequest": ".red-ui-palette-node[data-palette-type='http request']", - "websocketIn": ".red-ui-palette-node[data-palette-type='websocket in']", - "websocketOut": ".red-ui-palette-node[data-palette-type='websocket out']", - // sequence - "split": ".red-ui-palette-node[data-palette-type='split']", - "join": ".red-ui-palette-node[data-palette-type='join']", - "batch": ".red-ui-palette-node[data-palette-type='batch']", - // parser - "csv": ".red-ui-palette-node[data-palette-type='csv']", - "html": ".red-ui-palette-node[data-palette-type='html']", - "json": ".red-ui-palette-node[data-palette-type='json']", - "xml": ".red-ui-palette-node[data-palette-type='xml']", - "yaml": ".red-ui-palette-node[data-palette-type='yaml']", - // storage - "fileIn": ".red-ui-palette-node[data-palette-type='file in']", -}; - -function getId(type) { - return idMap[type]; -} - -module.exports = { - getId: getId, -}; diff --git a/test/editor/pageobjects/editor/workspace_page.js b/test/editor/pageobjects/editor/workspace_page.js deleted file mode 100644 index 92a725dac..000000000 --- a/test/editor/pageobjects/editor/workspace_page.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require("when"); -var events = require("nr-test-utils").require("@node-red/runtime/lib/events.js"); -var palette = require("./palette_page"); -var nodeFactory = require("../nodes/nodefactory_page"); -var keyPage = require("../util/key_page"); -var flowLayout = { - flowRightEnd : 600, - widthInterval : 300, - heightInterval : 80 -}; -var previousX = -flowLayout.widthInterval; -var previousY = 0; - -function addNode(type, x, y) { - if (x !== undefined) { - previousX = x; - if (y !== undefined) { - previousY = y; - } - } else { - if (previousX < flowLayout.flowRightEnd) { - previousX = previousX + flowLayout.widthInterval; - } else { - previousX = 0; - previousY = previousY + flowLayout.heightInterval; - } - } - browser.waitForVisible('#red-ui-palette-search'); - browser.setValue('//*[@id="red-ui-palette-search"]/div/form/input', type.replace(/([A-Z])/g, ' $1').toLowerCase()); - browser.pause(300); - browser.waitForVisible(palette.getId(type)); - browser.moveToObject(palette.getId(type)); - browser.buttonDown(); - browser.moveToObject("#red-ui-palette-search", previousX + 300, previousY + 100); // adjust to the top-left corner of workspace. - browser.buttonUp(); - // Last node is the one that has been created right now. - var nodeElement = browser.elements('//*[contains(concat(" ", normalize-space(@class), " "), " red-ui-flow-node-group ")][last()]'); - var nodeId = nodeElement.getAttribute('id'); - var node = nodeFactory.create(type, nodeId); - return node; -} - -function deleteAllNodes() { - browser.waitForVisible('//*[contains(@class, "active")]/a[@class="red-ui-tab-label"]'); - browser.click('//*[contains(@class, "active")]/a[@class="red-ui-tab-label"]'); - browser.pause(1000); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); -} - -function deploy() { - browser.call(function () { - return when.promise(function (resolve, reject) { - events.on("runtime-event", function (event) { - if (event.id === 'runtime-deploy') { - events.removeListener("runtime-event", arguments.callee); - resolve(); - } - }); - browser.clickWithWait('#red-ui-header-button-deploy'); - }); - }); - browser.waitForText('#red-ui-header-button-deploy', 10000); - // Need additional wait until buttons becomes clickable. - browser.pause(50); -} - -function init(width, height) { - deleteAllNodes(); - - if (width !== undefined) { - flowLayout.widthInterval = width; - } - if (height !== undefined) { - flowLayout.heightInterval = height; - } - previousX = -flowLayout.widthInterval; - previousY = 0; -} - -module.exports = { - addNode: addNode, - deploy: deploy, - init: init -}; diff --git a/test/editor/pageobjects/nodes/core/common/20-inject_page.js b/test/editor/pageobjects/nodes/core/common/20-inject_page.js deleted file mode 100644 index b7c230e6c..000000000 --- a/test/editor/pageobjects/nodes/core/common/20-inject_page.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function injectNode(id) { - nodePage.call(this, id); -} - -util.inherits(injectNode, nodePage); - -var payloadTypeList = { - "msg": 1, - "flow": 2, - "global": 3, - "str": 4, - "num": 5, - "bool": 6, - "json": 7, - "bin": 8, - "date": 9, - "jsonata": 10, - "env": 11, -}; - -var repeatTypeList = { - "none": 1, - "interval": 2, - "intervalBetweenTimes": 3, - "atASpecificTime": 4, -}; - -injectNode.prototype.setPayload = function(payloadType, payload) { - // Open a payload type list. - browser.clickWithWait('//*[@id="node-input-property-container"]/li[1]/div/div/div[3]'); - // Select a payload type. - var payloadTypeXPath = '//*[contains(@class, "red-ui-typedInput-options")]/a[' + payloadTypeList[payloadType] + ']'; - browser.clickWithWait(payloadTypeXPath); - if (payload) { - // Input a value. - browser.setValue('//*[@id="node-input-property-container"]/li[1]/div/div/div[3]/div[1]/input', payload); - } -} - -injectNode.prototype.setTopic = function(topic) { - browser.setValue('//*[@id="node-input-property-container"]/li[2]/div/div/div[3]/div[1]/input', topic); -} - -injectNode.prototype.setOnce = function(once) { - var isChecked = browser.isSelected('#node-input-once'); - if (isChecked !== once) { - browser.clickWithWait('#node-input-once'); - } -} - -injectNode.prototype.setRepeat = function(repeatType) { - var repeatTypeXPath = '//*[@id="inject-time-type-select"]/option[' + repeatTypeList[repeatType] + ']'; - browser.clickWithWait(repeatTypeXPath); -} - -injectNode.prototype.setRepeatInterval = function(repeat) { - browser.setValue('#inject-time-interval-count', repeat); -} - -module.exports = injectNode; diff --git a/test/editor/pageobjects/nodes/core/common/21-debug_page.js b/test/editor/pageobjects/nodes/core/common/21-debug_page.js deleted file mode 100644 index 3cec19985..000000000 --- a/test/editor/pageobjects/nodes/core/common/21-debug_page.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function debugNode(id) { - nodePage.call(this, id); -} - -util.inherits(debugNode, nodePage); - -debugNode.prototype.setOutput = function (complete) { - // Open a payload type list. - browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-container")]/button'); - if (complete !== 'true') { - // Select the "msg" type. - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options")][2]/a[1]'); - // Input the path in msg. - browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-input")]/input'); - browser.keys(Array('payload'.length).fill('Backspace')); - browser.setValue('//*[@id="dialog-form"]/div[1]/div/div[1]/input', complete); - } else { - // Select the "complete msg object" type. - browser.clickWithWait('/html/body/div[11]/a[2]'); - } -} - -module.exports = debugNode; diff --git a/test/editor/pageobjects/nodes/core/common/24-complete_page.js b/test/editor/pageobjects/nodes/core/common/24-complete_page.js deleted file mode 100644 index 558ee709a..000000000 --- a/test/editor/pageobjects/nodes/core/common/24-complete_page.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function completeNode(id) { - nodePage.call(this, id); -} - -util.inherits(completeNode, nodePage); - -completeNode.prototype.setScope = function (scope) { - if (scope) { - browser.clickWithWait('//*[@id="node-input-complete-target-select"]'); - browser.pause(1000); - if (Array.isArray(scope)) { - for (var i = 0; i < scope.length; i++) { - browser.moveToObject(scope[i].id); - browser.buttonDown(); - browser.buttonUp(); - } - } else { - browser.moveToObject(scope.id); - browser.buttonDown(); - browser.buttonUp(); - } - browser.clickWithWait('//*[contains(@class, "red-ui-notification")]/div/button[2]'); - browser.pause(1000); - } -} - -module.exports = completeNode; diff --git a/test/editor/pageobjects/nodes/core/common/25-catch_page.js b/test/editor/pageobjects/nodes/core/common/25-catch_page.js deleted file mode 100644 index 89bcb7f1e..000000000 --- a/test/editor/pageobjects/nodes/core/common/25-catch_page.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function catchNode(id) { - nodePage.call(this, id); -} - -util.inherits(catchNode, nodePage); - -catchNode.prototype.setScope = function (scope) { - if (scope) { - browser.selectWithWait('#node-input-scope-select', 'target'); - browser.clickWithWait('//*[@id="node-input-catch-target-select"]'); - browser.pause(1000); - if (Array.isArray(scope)) { - for (var i = 0; i < scope.length; i++) { - browser.moveToObject(scope[i].id); - browser.buttonDown(); - browser.buttonUp(); - } - } else { - browser.moveToObject(scope.id); - browser.buttonDown(); - browser.buttonUp(); - } - browser.clickWithWait('//*[contains(@class, "red-ui-notification")]/div/button[2]'); - browser.pause(1000); - } -} - -module.exports = catchNode; diff --git a/test/editor/pageobjects/nodes/core/common/25-status_page.js b/test/editor/pageobjects/nodes/core/common/25-status_page.js deleted file mode 100644 index 6de0fcde7..000000000 --- a/test/editor/pageobjects/nodes/core/common/25-status_page.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function statusNode(id) { - nodePage.call(this, id); -} - -util.inherits(statusNode, nodePage); - -statusNode.prototype.setScope = function (scope) { - if (scope) { - browser.selectWithWait('#node-input-scope-select', 'target'); - browser.clickWithWait('//*[@id="node-input-status-target-select"]'); - browser.pause(1000); - if (Array.isArray(scope)) { - for (var i = 0; i < scope.length; i++) { - browser.moveToObject(scope[i].id); - browser.buttonDown(); - browser.buttonUp(); - } - } else { - browser.moveToObject(scope.id); - browser.buttonDown(); - browser.buttonUp(); - } - browser.clickWithWait('//*[contains(@class, "red-ui-notification")]/div/button[2]'); - browser.pause(1000); - } -} - -module.exports = statusNode; diff --git a/test/editor/pageobjects/nodes/core/common/90-comment_page.js b/test/editor/pageobjects/nodes/core/common/90-comment_page.js deleted file mode 100644 index 2687dacfe..000000000 --- a/test/editor/pageobjects/nodes/core/common/90-comment_page.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function commentNode(id) { - nodePage.call(this, id); -} - -util.inherits(commentNode, nodePage); - -module.exports = commentNode; diff --git a/test/editor/pageobjects/nodes/core/function/10-function_page.js b/test/editor/pageobjects/nodes/core/function/10-function_page.js deleted file mode 100644 index c1bb80a80..000000000 --- a/test/editor/pageobjects/nodes/core/function/10-function_page.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var keyPage = require("../../../util/key_page"); - -function functionNode(id) { - nodePage.call(this, id); -} - -util.inherits(functionNode, nodePage); - -functionNode.prototype.setFunction = function (func) { - browser.clickWithWait('#node-input-func-editor'); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); - browser.keys(func); - // Delete the unnecessary code that ace editor does the autocompletion. - browser.keys(keyPage.selectToEnd()); - browser.keys(['Delete']); - // Need to wait until ace editor correctly checks the syntax. - browser.pause(300); -} - -module.exports = functionNode; diff --git a/test/editor/pageobjects/nodes/core/function/10-switch_page.js b/test/editor/pageobjects/nodes/core/function/10-switch_page.js deleted file mode 100644 index a04014063..000000000 --- a/test/editor/pageobjects/nodes/core/function/10-switch_page.js +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function switchNode(id) { - nodePage.call(this, id); -} - -util.inherits(switchNode, nodePage); - -var vtType = { - "msg": 1, - "flow": 2, - "global": 3, - "str": 4, - "num": 5, - "jsonata": 6, - "env": 7, - "prev": 8 -}; - -function setT(t, index) { - browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t); -} - -function setV(v, vt, index) { - if (vt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[vt] + ']'); - } - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', v); - } -} - -function setBetweenV(v, vt, v2, v2t, index) { - if (vt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[vt] + ']'); - } - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v); - } - if (v2t) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[v2t] + ']'); - } - if (v2) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div/div[1]/input', v2); - } -} - -function setSequenceV(v, vt, index) { - var sType = { - "flow": 1, - "global": 2, - "num": 3, - "jsonata": 4, - "env": 5, - }; - - if (vt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + sType[vt] + ']'); - } - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v); - } -} - -switchNode.prototype.ruleEqual = function (v, vt, index) { - index = index || 1; - setT('eq', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleNotEqual = function (v, vt, index) { - index = index || 1; - setT('neq', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleLessThan = function (v, vt, index) { - index = index || 1; - setT('lt', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleLessThanOrEqual = function (v, vt, index) { - index = index || 1; - setT('lte', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleGreaterThan = function (v, vt, index) { - index = index || 1; - setT('gt', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleGreaterThanOrEqual = function (v, vt, index) { - index = index || 1; - setT('gte', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleHasKey = function (v, vt, index) { - index = index || 1; - setT('hask', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleIsBetween = function (v, vt, v2, v2t, index) { - index = index || 1; - setT('btwn', index); - setBetweenV(v, vt, v2, v2t, index); -} - -switchNode.prototype.ruleContains = function (v, vt, index) { - index = index || 1; - setT('cont', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleMatchesRegex = function (v, vt, index) { - index = index || 1; - setT('regex', index); - setV(v, vt, index); -} - -switchNode.prototype.ruleIsTrue = function (index) { - index = index || 1; - setT('true', index); -} - -switchNode.prototype.ruleIsFalse = function (index) { - index = index || 1; - setT('false', index); -} - -switchNode.prototype.ruleIsNull = function (index) { - index = index || 1; - setT('null', index); -} - -switchNode.prototype.ruleIsNotNull = function (index) { - index = index || 1; - setT('nnull', index); -} - -switchNode.prototype.ruleIsOfType = function (t, index) { - index = index || 1; - setT('istype', index); - - var tType = { - "str": 1, - "num": 2, - "boolean": 3, - "array": 4, - "buffer": 5, - "object": 6, - "json": 7, - "undefined": 8, - "null": 9 - }; - - if (t) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + tType[t] + ']'); - } -} - -switchNode.prototype.ruleIsEmpty = function (index) { - index = index || 1; - setT('empty', index); -} - -switchNode.prototype.ruleIsNotEmpty = function (index) { - index = index || 1; - setT('nempty', index); -} - -switchNode.prototype.ruleHead = function (v, vt, index) { - index = index || 1; - setT('head', index); - setSequenceV(v, vt, index); -} - -switchNode.prototype.ruleIndexBetween = function (v, vt, v2, v2t, index) { - index = index || 1; - setT('index', index); - setBetweenV(v, vt, v2, v2t, index); -} - -switchNode.prototype.ruleTail = function (v, vt, index) { - index = index || 1; - setT('tail', index); - setSequenceV(v, vt, index); -} - -switchNode.prototype.ruleJSONataExp = function (v, index) { - index = index || 1; - setT('jsonata_exp', index); - if (v) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v); - } -} - -switchNode.prototype.ruleOtherwise = function (index) { - index = index || 1; - setT('else', index); -} - -switchNode.prototype.addRule = function () { - browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a'); -} - -module.exports = switchNode; diff --git a/test/editor/pageobjects/nodes/core/function/15-change_page.js b/test/editor/pageobjects/nodes/core/function/15-change_page.js deleted file mode 100644 index eb26f48aa..000000000 --- a/test/editor/pageobjects/nodes/core/function/15-change_page.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function changeNode(id) { - nodePage.call(this, id); -} - -util.inherits(changeNode, nodePage); - -var totType = { - "msg": 1, - "flow": 2, - "global": 3, - "str": 4, - "num": 5, - "bool": 6, - "json": 7, - "bin": 8, - "date": 9, - "jsonata": 10, - "env": 11, -}; - -var ptType = { - "msg": 1, - "flow": 2, - "global": 3, -}; - -function setT(t, index) { - browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t); -} - -// It is better to create a function whose input value is the object type in the future, -changeNode.prototype.ruleSet = function (p, pt, to, tot, index) { - index = index || 1; - setT('set', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } - if (tot) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[tot] + ']'); - } - if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/div/input', to); - } -} - -changeNode.prototype.ruleChange = function (p, pt, from, fromt, to, tot, index) { - index = index || 1; - setT('change', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } - if (fromt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[1]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']'); - } - if (from) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[1]/div[2]/div[1]/input', from); - } - if (tot) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[2]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']'); - } - if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[2]/div[2]/div[1]/input', to); - } -} - -changeNode.prototype.ruleDelete = function (p, pt, index) { - index = index || 1; - setT('delete', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } -} - -changeNode.prototype.ruleMove = function (p, pt, to, tot, index) { - index = index || 1; - setT('move', index); - if (pt) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']'); - } - if (p) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - } - if (tot) { - browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']'); - } - if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/div/input', to); - } -} - -changeNode.prototype.addRule = function () { - browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a'); -} - -module.exports = changeNode; diff --git a/test/editor/pageobjects/nodes/core/function/16-range_page.js b/test/editor/pageobjects/nodes/core/function/16-range_page.js deleted file mode 100644 index 230929995..000000000 --- a/test/editor/pageobjects/nodes/core/function/16-range_page.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function rangeNode(id) { - nodePage.call(this, id); -} - -util.inherits(rangeNode, nodePage); - -rangeNode.prototype.setAction = function(action) { - browser.selectWithWait('#node-input-action', action); -} - -rangeNode.prototype.setRange = function(minin, maxin, minout, maxout) { - browser.setValue('#node-input-minin', minin); - browser.setValue('#node-input-maxin', maxin); - browser.setValue('#node-input-minout', minout); - browser.setValue('#node-input-maxout', maxout); -} - -module.exports = rangeNode; diff --git a/test/editor/pageobjects/nodes/core/function/80-template_page.js b/test/editor/pageobjects/nodes/core/function/80-template_page.js deleted file mode 100644 index bf1786ba2..000000000 --- a/test/editor/pageobjects/nodes/core/function/80-template_page.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var keyPage = require("../../../util/key_page"); - -function templateNode(id) { - nodePage.call(this, id); -} - -util.inherits(templateNode, nodePage); - -templateNode.prototype.setSyntax = function (syntax) { - browser.selectWithWait('#node-input-syntax', syntax); -} - -templateNode.prototype.setFormat = function (format) { - browser.selectWithWait('#node-input-format', format); -} - -templateNode.prototype.setTemplate = function (template) { - browser.clickWithWait('#node-input-template-editor'); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); - browser.keys(template); - browser.keys(keyPage.selectToEnd()); - browser.keys(['Delete']); - // Need to wait until ace editor correctly checks the syntax. - browser.pause(300); -} - -module.exports = templateNode; diff --git a/test/editor/pageobjects/nodes/core/function/89-delay_page.js b/test/editor/pageobjects/nodes/core/function/89-delay_page.js deleted file mode 100644 index 3604beb67..000000000 --- a/test/editor/pageobjects/nodes/core/function/89-delay_page.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function delayNode(id) { - nodePage.call(this, id); -} - -util.inherits(delayNode, nodePage); - -delayNode.prototype.setTimeout = function (timeout) { - browser.setValue('//*[@id="node-input-timeout"]', timeout); -} - -module.exports = delayNode; diff --git a/test/editor/pageobjects/nodes/core/function/89-trigger_page.js b/test/editor/pageobjects/nodes/core/function/89-trigger_page.js deleted file mode 100644 index 5d24de380..000000000 --- a/test/editor/pageobjects/nodes/core/function/89-trigger_page.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function triggerNode(id) { - nodePage.call(this, id); -} - -util.inherits(triggerNode, nodePage); - -triggerNode.prototype.setSend = function (send, sendt) { - var sendType = { - "flow": 1, - "global": 2, - "str": 3, - "num": 4, - "bool": 5, - "json": 6, - "bin": 7, - "date": 8, - "env": 9, - "pay": 10, - "nul": 11 - }; - - if (sendt) { - browser.clickWithWait('//*[@id="dialog-form"]/div[1]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + sendType[sendt] + ']'); - } - if (send) { - browser.setValue('//*[@id="dialog-form"]/div[1]/div/div[1]/input', send); - } -} - -triggerNode.prototype.setDuration = function (duration, units) { - browser.setValue('//*[@id="node-input-duration"]', duration); - if (units) { - browser.selectWithWait('//*[@id="node-input-units"]', units); - } -} - -triggerNode.prototype.setThenSend = function (thenSend, thenSendt) { - var thenSendType = { - "flow": 1, - "global": 2, - "str": 3, - "num": 4, - "bool": 5, - "json": 6, - "bin": 7, - "date": 8, - "env": 9, - "pay": 10, - "payl": 11, - "nul": 12 - }; - - if (thenSendt) { - browser.clickWithWait('//*[@id="dialog-form"]/div[5]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + thenSendType[thenSendt] + ']'); - } - if (thenSend) { - browser.setValue('//*[@id="dialog-form"]/div[5]/div/div[1]/input', thenSend); - } -} - -module.exports = triggerNode; diff --git a/test/editor/pageobjects/nodes/core/function/90-exec_page.js b/test/editor/pageobjects/nodes/core/function/90-exec_page.js deleted file mode 100644 index 69b8b6c9a..000000000 --- a/test/editor/pageobjects/nodes/core/function/90-exec_page.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function execNode(id) { - nodePage.call(this, id); -} - -util.inherits(execNode, nodePage); - -execNode.prototype.setCommand = function (command) { - browser.setValue('//*[@id="node-input-command"]', command); -} - -execNode.prototype.setAppend = function (checkbox) { - if (browser.isSelected('#node-input-addpay') !== checkbox) { - browser.click('#node-input-addpay'); - } -} - -module.exports = execNode; diff --git a/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js b/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js deleted file mode 100644 index 4bdd92336..000000000 --- a/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var mqttBrokerNode = {}; - -mqttBrokerNode.setServer = function (broker, port) { - browser.setValue('#node-config-input-broker', broker); - port = port ? port : 1883; - browser.setValue('#node-config-input-port', port); -}; - -mqttBrokerNode.clickOk = function () { - browser.clickWithWait('#node-config-dialog-ok'); - // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 10000, true); -}; - -mqttBrokerNode.edit = function () { - browser.waitForVisible('#node-input-lookup-broker'); - browser.click('#node-input-lookup-broker'); - // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 10000); -}; - -function mqttInNode(id) { - nodePage.call(this, id); -} - -util.inherits(mqttInNode, nodePage); - -mqttInNode.prototype.setTopic = function (topic) { - browser.setValue('#node-input-topic', topic); -}; - -mqttInNode.prototype.setQoS = function (qos) { - browser.selectWithWait('#node-input-qos', qos); -}; - -mqttInNode.prototype.mqttBrokerNode = mqttBrokerNode; -module.exports.mqttInNode = mqttInNode; - -function mqttOutNode(id) { - nodePage.call(this, id); -} - -util.inherits(mqttOutNode, nodePage); - -mqttOutNode.prototype.setTopic = function (topic) { - browser.setValue('#node-input-topic', topic); -}; - -mqttOutNode.prototype.setRetain = function (retain) { - browser.selectWithWait('#node-input-retain', retain); -}; - -mqttOutNode.prototype.mqttBrokerNode = mqttBrokerNode; -module.exports.mqttOutNode = mqttOutNode; diff --git a/test/editor/pageobjects/nodes/core/network/21-httpin_page.js b/test/editor/pageobjects/nodes/core/network/21-httpin_page.js deleted file mode 100644 index 9454ef034..000000000 --- a/test/editor/pageobjects/nodes/core/network/21-httpin_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function httpInNode(id) { - nodePage.call(this, id); -} - -util.inherits(httpInNode, nodePage); - -httpInNode.prototype.setMethod = function(method) { - browser.selectWithWait('#node-input-method', method); -} - -httpInNode.prototype.setUrl = function(url) { - browser.setValue('#node-input-url', url); -} - -module.exports = httpInNode; diff --git a/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js b/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js deleted file mode 100644 index b2ca812e3..000000000 --- a/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function httpRequestNode(id) { - nodePage.call(this, id); -} - -util.inherits(httpRequestNode, nodePage); - -httpRequestNode.prototype.setUrl = function(url) { - browser.setValue('#node-input-url', url); -} - -httpRequestNode.prototype.setMethod = function(method) { - browser.selectWithWait('#node-input-method', method); -} - -httpRequestNode.prototype.setReturn = function(ret) { - browser.selectWithWait('#node-input-ret', ret); -} - -module.exports = httpRequestNode; diff --git a/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js b/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js deleted file mode 100644 index 7fb1886ce..000000000 --- a/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function httpResponseNode(id) { - nodePage.call(this, id); -} - -util.inherits(httpResponseNode, nodePage); - -module.exports = httpResponseNode; diff --git a/test/editor/pageobjects/nodes/core/network/22-websocket_page.js b/test/editor/pageobjects/nodes/core/network/22-websocket_page.js deleted file mode 100644 index 8f7dc261e..000000000 --- a/test/editor/pageobjects/nodes/core/network/22-websocket_page.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -var websocketListenerNode = {}; - -websocketListenerNode.setPath = function (path) { - browser.setValue('#node-config-input-path', path); -}; - -websocketListenerNode.setSendReceive = function (wholemsg) { - browser.selectWithWait('#node-config-input-wholemsg', wholemsg); -}; - -websocketListenerNode.clickOk = function () { - browser.clickWithWait('#node-config-dialog-ok'); - // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 10000, true); -}; - -websocketListenerNode.edit = function () { - browser.waitForVisible('#node-input-lookup-server'); - browser.click('#node-input-lookup-server'); - // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 10000); -}; - -var websocketClientNode = {}; - -websocketClientNode.setPath = function (path) { - browser.setValue('#node-config-input-path', path); -}; - -websocketClientNode.setSendReceive = function (wholemsg) { - browser.selectWithWait('#node-config-input-wholemsg', wholemsg); -}; - -websocketClientNode.clickOk = function () { - browser.clickWithWait('#node-config-dialog-ok'); - // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 10000, true); -}; - -websocketClientNode.edit = function () { - browser.waitForVisible('#node-input-lookup-client'); - browser.click('#node-input-lookup-client'); - // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 10000); -}; - -function websocketInNode(id) { - nodePage.call(this, id); -} - -util.inherits(websocketInNode, nodePage); - -websocketInNode.prototype.setType = function (type) { - browser.selectWithWait('#node-input-mode', type); -}; - -websocketInNode.prototype.websocketListenerNode = websocketListenerNode; -websocketInNode.prototype.websocketClientNode = websocketClientNode; -module.exports.websocketInNode = websocketInNode; - -function websocketOutNode(id) { - nodePage.call(this, id); -} - -util.inherits(websocketOutNode, nodePage); - -websocketOutNode.prototype.setType = function (type) { - browser.selectWithWait('#node-input-mode', type); -}; - -websocketOutNode.prototype.websocketListenerNode = websocketListenerNode; -websocketOutNode.prototype.websocketClientNode = websocketClientNode; -module.exports.websocketOutNode = websocketOutNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js b/test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js deleted file mode 100644 index e4bc9502c..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-CSV_page.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function csvNode(id) { - nodePage.call(this, id); -} - -util.inherits(csvNode, nodePage); - -csvNode.prototype.setColumns = function (columns) { - browser.setValue('#node-input-temp', columns); -} - -csvNode.prototype.setSkipLines = function (skip) { - browser.setValue('#node-input-skip', skip); -} - -csvNode.prototype.setFirstRow4Names = function (checkbox) { - if (browser.isSelected('#node-input-hdrin') !== checkbox) { - browser.click('#node-input-hdrin'); - } -} - -csvNode.prototype.setOutput = function (output) { - browser.selectWithWait('#node-input-multi', output); -} - -csvNode.prototype.setIncludeRow = function (checkbox) { - if (browser.isSelected('#node-input-hdrout') !== checkbox) { - browser.click('#node-input-hdrout'); - } -} - -module.exports = csvNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js b/test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js deleted file mode 100644 index 243e4c905..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-HTML_page.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function htmlNode(id) { - nodePage.call(this, id); -} - -util.inherits(htmlNode, nodePage); - -htmlNode.prototype.setSelector = function (tag) { - browser.setValue('#node-input-tag', tag); -} - -module.exports = htmlNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js b/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js deleted file mode 100644 index e0b31dd36..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function jsonNode(id) { - nodePage.call(this, id); -} - -util.inherits(jsonNode, nodePage); - -jsonNode.prototype.setAction = function (action) { - browser.setValue('node-input-action', action); -} - -jsonNode.prototype.setProperty = function (property) { - browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); -} - -module.exports = jsonNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js b/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js deleted file mode 100644 index 696ec59cb..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function xmlNode(id) { - nodePage.call(this, id); -} - -util.inherits(xmlNode, nodePage); - -xmlNode.prototype.setAction = function (action) { - browser.setValue('node-input-action', action); -} - -xmlNode.prototype.setProperty = function (property) { - browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); -} - -module.exports = xmlNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js b/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js deleted file mode 100644 index 1002f3eb4..000000000 --- a/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function yamlNode(id) { - nodePage.call(this, id); -} - -util.inherits(yamlNode, nodePage); - -yamlNode.prototype.setAction = function (action) { - browser.setValue('node-input-action', action); -} - -yamlNode.prototype.setProperty = function (property) { - browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); -} - -module.exports = yamlNode; diff --git a/test/editor/pageobjects/nodes/core/sequence/17-split_page.js b/test/editor/pageobjects/nodes/core/sequence/17-split_page.js deleted file mode 100644 index 8fc32ab1e..000000000 --- a/test/editor/pageobjects/nodes/core/sequence/17-split_page.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function splitNode(id) { - nodePage.call(this, id); -} - -util.inherits(splitNode, nodePage); - -splitNode.prototype.setSplitUsing = function (splt, spltt) { - var spltType = { - "str": 1, - "bin": 2, - "len": 3 - }; - - browser.clickWithWait('//*[@id="dialog-form"]/div[3]/div/button[1]'); - browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + spltType[spltt] + ']'); - browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', splt); -}; - -module.exports.splitNode = splitNode; - -function joinNode(id) { - nodePage.call(this, id); -} - -util.inherits(joinNode, nodePage); - -module.exports.joinNode = joinNode; diff --git a/test/editor/pageobjects/nodes/core/sequence/19-batch_page.js b/test/editor/pageobjects/nodes/core/sequence/19-batch_page.js deleted file mode 100644 index 0d7b13028..000000000 --- a/test/editor/pageobjects/nodes/core/sequence/19-batch_page.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require('util'); - -var nodePage = require('../../node_page'); - -function batchNode(id) { - nodePage.call(this, id); -} - -batchNode.prototype.setMode = function (mode) { - browser.selectWithWait('#node-input-mode', mode); -} - -batchNode.prototype.setCount = function (count) { - browser.setValue('#node-input-count', count); -} - -batchNode.prototype.setOverlap = function (overlap) { - browser.setValue('#node-input-overlap', overlap); -} - -util.inherits(batchNode, nodePage); - -module.exports = batchNode; diff --git a/test/editor/pageobjects/nodes/core/storage/10-filein_page.js b/test/editor/pageobjects/nodes/core/storage/10-filein_page.js deleted file mode 100644 index 942ea63d8..000000000 --- a/test/editor/pageobjects/nodes/core/storage/10-filein_page.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var util = require("util"); - -var nodePage = require("../../node_page"); - -function fileInNode(id) { - nodePage.call(this, id); -} - -util.inherits(fileInNode, nodePage); - -var formatType = { - "utf8": 1, - "lines": 2, - "": 3, // a single Buffer object - "stream": 4 -}; - -fileInNode.prototype.setFilename = function(filename) { - browser.setValue('#node-input-filename', filename); -} - -fileInNode.prototype.setOutput = function(format) { - browser.clickWithWait('#node-input-format'); - var formatTypeXPath = '//*[@id="node-input-format"]/option[' + formatType[format] + ']'; - browser.clickWithWait(formatTypeXPath); -} - -module.exports = fileInNode; diff --git a/test/editor/pageobjects/nodes/node_page.js b/test/editor/pageobjects/nodes/node_page.js deleted file mode 100644 index 03e734cab..000000000 --- a/test/editor/pageobjects/nodes/node_page.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -function Node(id) { - this.id = '//*[@id="' + id + '"]'; -} - -Node.prototype.edit = function () { - browser.waitForVisible(this.id); - browser.moveToObject(this.id); - browser.buttonDown(); - browser.buttonUp(); - browser.keys(['Enter']); - // Wait until an edit dialog opens. - browser.waitForVisible('#node-dialog-ok', 10000); -} - -Node.prototype.clickOk = function () { - browser.clickWithWait('#node-dialog-ok'); - // Wait untile an edit dialog closes. - browser.waitForVisible('#node-dialog-ok', 10000, true); - browser.pause(50); -} - -Node.prototype.connect = function (targetNode, port) { - port = port || 1; - var outputPort = this.id + '/*[@class="red-ui-flow-port-output"][' + port + ']'; - var inputPort = targetNode.id + '/*[@class="red-ui-flow-port-input"]'; - browser.dragAndDrop(outputPort, inputPort); -} - -Node.prototype.clickLeftButton = function () { - browser.moveToObject(this.id + '/*[@class="red-ui-flow-node-button"]'); - browser.buttonDown(); - browser.buttonUp(); -} - -module.exports = Node; diff --git a/test/editor/pageobjects/nodes/nodefactory_page.js b/test/editor/pageobjects/nodes/nodefactory_page.js deleted file mode 100644 index 008ecc625..000000000 --- a/test/editor/pageobjects/nodes/nodefactory_page.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var injectNode = require('./core/common/20-inject_page'); -var debugNode = require('./core/common/21-debug_page'); -var completeNode = require('./core/common/24-complete_page'); -var catchNode = require('./core/common/25-catch_page'); -var statusNode = require('./core/common/25-status_page'); -var commentNode = require('./core/common/90-comment_page'); -var functionNode = require('./core/function/10-function_page'); -var switchNode = require('./core/function/10-switch_page'); -var changeNode = require('./core/function/15-change_page'); -var rangeNode = require('./core/function/16-range_page'); -var templateNode = require('./core/function/80-template_page'); -var delayNode = require('./core/function/89-delay_page'); -var triggerNode = require('./core/function/89-trigger_page'); -var execNode = require('./core/function/90-exec_page'); -var mqttInNode = require('./core/network/10-mqtt_page').mqttInNode; -var mqttOutNode = require('./core/network/10-mqtt_page').mqttOutNode; -var httpInNode = require('./core/network/21-httpin_page'); -var httpResponseNode = require('./core/network/21-httpresponse_page'); -var httpRequestNode = require('./core/network/21-httprequest_page'); -var websocketInNode = require('./core/network/22-websocket_page').websocketInNode; -var websocketOutNode = require('./core/network/22-websocket_page').websocketOutNode; -var splitNode = require('./core/sequence/17-split_page').splitNode; -var joinNode = require('./core/sequence/17-split_page').joinNode; -var batchNode = require('./core/sequence/19-batch_page'); -var csvNode = require('./core/parsers/70-CSV_page'); -var htmlNode = require('./core/parsers/70-HTML_page'); -var jsonNode = require('./core/parsers/70-JSON_page'); -var xmlNode = require('./core/parsers/70-XML_page'); -var yamlNode = require('./core/parsers/70-YAML_page'); -var fileInNode = require('./core/storage/10-filein_page'); - -var nodeCatalog = { - // common - "inject": injectNode, - "debug": debugNode, - "complete": completeNode, - "catch": catchNode, - "status": statusNode, - "comment": commentNode, - // function - "function": functionNode, - "switch": switchNode, - "change": changeNode, - "range": rangeNode, - "template": templateNode, - "delay": delayNode, - "trigger": triggerNode, - "exec": execNode, - // network - "mqttIn": mqttInNode, - "mqttOut": mqttOutNode, - "httpIn": httpInNode, - "httpResponse": httpResponseNode, - "httpRequest": httpRequestNode, - "websocketIn": websocketInNode, - "websocketOut": websocketOutNode, - // sequence - "split": splitNode, - "join": joinNode, - "batch": batchNode, - // parser - "csv": csvNode, - "html": htmlNode, - "json": jsonNode, - "xml": xmlNode, - "yaml": yamlNode, - // storage - "fileIn": fileInNode -}; - -function create(type, id) { - var Node = nodeCatalog[type]; - return new Node(id); -} - -module.exports = { - create: create, -}; diff --git a/test/editor/pageobjects/util/key_page.js b/test/editor/pageobjects/util/key_page.js deleted file mode 100644 index 497a8a141..000000000 --- a/test/editor/pageobjects/util/key_page.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var os = require("os"); - -var shortCutKeyMap = { - "selectAll": ['Control', 'a', 'a', 'Control'], - "selectToEnd": ['Control', 'Shift', 'End', 'Shift', 'Control'], -}; - -var shortCutKeyMapForMac = { - "selectAll": ['Command', 'a', 'a', 'Command'], - "selectToEnd": ['Command', 'Shift', 'ArrowDown', 'Shift', 'Command'], -}; - -function getShortCutKey(type) { - if (process.env.BROWSERSTACK) { - if (browser.desiredCapabilities.os === 'OS X') { - return shortCutKeyMapForMac[type]; - } - return shortCutKeyMap[type]; - } - if (os.type() === 'Darwin') { - return shortCutKeyMapForMac[type]; - } - return shortCutKeyMap[type]; -} - -function selectAll() { - var key = getShortCutKey('selectAll'); - return key; -} - -function selectToEnd() { - var key = getShortCutKey('selectToEnd'); - return key; -} - -module.exports = { - selectAll: selectAll, - selectToEnd: selectToEnd, -}; diff --git a/test/editor/pageobjects/util/spec_util_page.js b/test/editor/pageobjects/util/spec_util_page.js deleted file mode 100644 index f64743664..000000000 --- a/test/editor/pageobjects/util/spec_util_page.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -function pause(msec) { - browser.pause(msec); -} - - module.exports = { - pause: pause, - }; diff --git a/test/editor/pageobjects/util/util_page.js b/test/editor/pageobjects/util/util_page.js deleted file mode 100644 index 3a764eb93..000000000 --- a/test/editor/pageobjects/util/util_page.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var waitTime = 5000; - -function repeatUntilSuccess(operation, args) { - // Wait at most 10 seconds. - for (var i = 0; i < 200; i++) { - try { - var ret = operation(args); - return ret; - } catch (e) { - if (i === 199) { - console.trace(); - throw e; - } - browser.pause(50); - } - } -} - -function init() { - browser.addCommand("clickWithWait", function(selector) { - try { - // This is necessary because there is a case that the target may exist but still moving. - browser.pause(50); - browser.waitForVisible(selector); - - var ret = repeatUntilSuccess(function(selector) { - return browser.click(selector); - }, selector); - return ret; - } catch (e) { - console.trace(); - throw e; - } - }, false); - - browser.addCommand("getTextWithWait", function(selector) { - try { - browser.waitForExist(selector); - browser.waitForValue(selector); - - var ret = repeatUntilSuccess(function(selector) { - return browser.getText(selector); - }, selector); - return ret; - } catch (e) { - console.trace(); - throw e; - } - }, false); - - browser.addCommand("selectWithWait", function(selector, value) { - try { - browser.waitForVisible(selector, waitTime); - - var ret = repeatUntilSuccess(function(args) { - return browser.selectByValue(args[0], args[1]); - }, [selector, value.toString()]); - return ret; - } catch (e) { - console.trace(); - throw e; - } - }, false); -} - - module.exports = { - init: init, - }; diff --git a/test/editor/specs/editor/workspace_uispec.js b/test/editor/specs/editor/workspace_uispec.js deleted file mode 100644 index 15de1a044..000000000 --- a/test/editor/specs/editor/workspace_uispec.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require("should"); -var fs = require('fs-extra'); - -var helper = require("../../editor_helper"); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); - - -describe('Workspace', function() { - beforeEach(function() { - workspace.init(); - }); - - before(function() { - helper.startServer(); - }); - - after(function() { - helper.stopServer(); - }); - - it('should have a right title', function () { - browser.getTitle().should.startWith('Node-RED'); - }); - - it('should output a timestamp', function() { - var injectNode = workspace.addNode("inject"); - var debugNode = workspace.addNode("debug"); - injectNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.within(1500000000000, 3000000000000); - }); - -}); diff --git a/test/editor/specs/scenario/cookbook_dataformats_uispec.js b/test/editor/specs/scenario/cookbook_dataformats_uispec.js deleted file mode 100644 index 8321bd924..000000000 --- a/test/editor/specs/scenario/cookbook_dataformats_uispec.js +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('working with data formats', function () { - it('convert to/from JSON', function () { - var injectNode1 = workspace.addNode('inject'); - var jsonNode1 = workspace.addNode('json'); - var debugNode1 = workspace.addNode('debug'); - - injectNode1.edit(); - injectNode1.setPayload('str', '{"a":1}'); - injectNode1.clickOk(); - - jsonNode1.edit(); - jsonNode1.setProperty('payload'); - jsonNode1.clickOk(); - - injectNode1.connect(jsonNode1); - jsonNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject'); - var jsonNode2 = workspace.addNode('json'); - var debugNode2 = workspace.addNode('debug'); - - injectNode2.edit(); - injectNode2.setPayload('json', '{"a":1}'); - injectNode2.clickOk(); - - jsonNode2.edit(); - jsonNode2.setProperty('payload'); - jsonNode2.clickOk(); - - injectNode2.connect(jsonNode2); - jsonNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.eql('1'); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.eql('"{"a":1}"'); - }); - - it('convert to/from XML', function () { - var injectNode1 = workspace.addNode('inject', 0); - var templateNode1 = workspace.addNode('template', 200); - var xmlNode1 = workspace.addNode('xml', 400); - var debugNode1 = workspace.addNode('debug', 600); - - injectNode1.edit(); - injectNode1.setPayload('str', '{"a":1}'); - injectNode1.clickOk(); - - templateNode1.edit(); - templateNode1.setFormat('text'); - templateNode1.setSyntax('plain'); - templateNode1.setTemplate('' - + ' Nick' - + ' Dave' - + ' Reminder' - + ' Update the website' - + ''); - templateNode1.clickOk(); - - xmlNode1.edit(); - xmlNode1.setProperty('payload'); - xmlNode1.clickOk(); - - injectNode1.connect(templateNode1); - templateNode1.connect(xmlNode1); - xmlNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject'); - var xmlNode2 = workspace.addNode('xml'); - var debugNode2 = workspace.addNode('debug'); - - injectNode2.edit(); - injectNode2.setPayload('json', '{' - + ' "note": {' - + ' "$": { "priority": "high" },' - + ' "to": [ "Nick" ],' - + ' "from": [ "Dave" ],' - + ' "heading": [ "Reminder" ],' - + ' "body": [ "Update the website" ]' - + ' }' - + '}'); - injectNode2.clickOk(); - - xmlNode2.edit(); - xmlNode2.setProperty('payload'); - xmlNode2.clickOk(); - - injectNode2.connect(xmlNode2); - xmlNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.eql('object'); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.eql('"' - + '' - + 'Nick' - + 'Dave' - + 'Reminder' - + 'Update the website' - + '"'); - }); - - it('convert to/from YAML', function () { - var injectNode1 = workspace.addNode('inject', 0); - var templateNode1 = workspace.addNode('template', 200); - var yamlNode1 = workspace.addNode('yaml', 400); - var debugNode1 = workspace.addNode('debug', 600); - - injectNode1.edit(); - injectNode1.setPayload('str', '{"a":1}'); - injectNode1.clickOk(); - - templateNode1.edit(); - templateNode1.setFormat('yaml'); - templateNode1.setSyntax('plain'); - templateNode1.setTemplate('a: 1\n' - + 'b:\n' - + ' - 1\n' - + '- 2\n' - + '- 3'); - templateNode1.clickOk(); - - yamlNode1.edit(); - yamlNode1.setProperty('payload'); - yamlNode1.clickOk(); - - injectNode1.connect(templateNode1); - templateNode1.connect(yamlNode1); - yamlNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject'); - var yamlNode2 = workspace.addNode('yaml'); - var debugNode2 = workspace.addNode('debug'); - - injectNode2.edit(); - injectNode2.setPayload('json', '{"a":1, "b":[1,2,3]}'); - injectNode2.clickOk(); - - yamlNode2.edit(); - yamlNode2.setProperty('payload'); - yamlNode2.clickOk(); - - injectNode2.connect(yamlNode2); - yamlNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.eql([ '1', 'array[3]' ]); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.eql('"a: 1↵b:↵ - 1↵ - 2↵ - 3↵"'); - }); - - it('generate CSV output', function () { - var injectNode1 = workspace.addNode('inject', 0); - var changeNode1 = workspace.addNode('change', 200); - var csvNode1 = workspace.addNode('csv', 400); - var debugNode1 = workspace.addNode('debug', 600); - - changeNode1.edit(); - changeNode1.ruleSet('payload', 'msg', '{' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + '}', 'jsonata'); - changeNode1.clickOk(); - - csvNode1.edit(); - csvNode1.setColumns('a,b,c'); - csvNode1.clickOk(); - - injectNode1.connect(changeNode1); - changeNode1.connect(csvNode1); - csvNode1.connect(debugNode1); - - var injectNode2 = workspace.addNode('inject', 0, 80); - var changeNode2 = workspace.addNode('change', 200, 80); - var csvNode2 = workspace.addNode('csv', 400, 80); - var debugNode2 = workspace.addNode('debug', 600, 80); - - changeNode2.edit(); - changeNode2.ruleSet('payload', 'msg', '[' - + ' {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }, {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }, {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }, {' - + ' "a": $floor(100*$random()),' - + ' "b": $floor(100*$random()),' - + ' "c": $floor(100*$random())' - + ' }' - + ']', 'jsonata'); - changeNode2.clickOk(); - - csvNode2.edit(); - csvNode2.setColumns('a,b,c'); - csvNode2.setIncludeRow(true); - csvNode2.clickOk(); - - injectNode2.connect(changeNode2); - changeNode2.connect(csvNode2); - csvNode2.connect(debugNode2); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage().should.match(/^"([1-9]?[0-9],){2}[1-9]?[0-9]↵"$/); - debugTab.clearMessage(); - injectNode2.clickLeftButton(); - debugTab.getMessage().should.match(/^"(([1-9]?[0-9],){2}[1-9]?[0-9]↵){4}"$/); - }); - - it('parse CSV input', function () { - var injectNode = workspace.addNode('inject'); - var templateNode = workspace.addNode('template'); - var csvNode = workspace.addNode('csv'); - var debugNode = workspace.addNode('debug'); - - templateNode.edit(); - templateNode.setFormat('handlebars'); - templateNode.setSyntax('mustache'); - templateNode.setTemplate('# This is some random data\n' - + 'a,b,c\n' - + '80,18,2\n' - + '52,36,10\n' - + '91,18,61\n' - + '32,47,65'); - templateNode.clickOk(); - - csvNode.edit(); - csvNode.setSkipLines(1); - csvNode.setFirstRow4Names(true); - csvNode.setOutput('mult'); - csvNode.clickOk(); - - injectNode.connect(templateNode); - templateNode.connect(csvNode); - csvNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql([ 'object', 'object', 'object', 'object' ]); - }); - - it('simple GET request', function () { - var injectNode = workspace.addNode('inject'); - var httpRequestNode = workspace.addNode('httpRequest'); - var htmlNode = workspace.addNode('html'); - var debugNode = workspace.addNode('debug'); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - httpRequestNode.setUrl('https://nodered.org'); - httpRequestNode.clickOk(); - - htmlNode.edit(); - htmlNode.setSelector('.node-red-latest-version'); - htmlNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(htmlNode); - htmlNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.match(/^"v[0-9]+\.[0-9]+\.[0-9]"$/); - }); - - it('split text into one message per line', function () { - var injectNode = workspace.addNode('inject'); - var templateNode = workspace.addNode('template'); - var splitNode = workspace.addNode('split'); - var changeNode = workspace.addNode('change'); - var joinNode = workspace.addNode('join'); - var debugNode = workspace.addNode('debug'); - - templateNode.edit(); - templateNode.setFormat('handlebars'); - templateNode.setSyntax('mustache'); - templateNode.setTemplate('one\ntwo\nthree\nfour\nfive'); - templateNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('payload', 'msg', '(parts.index+1) & ": " & payload', 'jsonata'); - changeNode.clickOk(); - - injectNode.connect(templateNode); - templateNode.connect(splitNode); - splitNode.connect(changeNode); - changeNode.connect(joinNode); - joinNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"1: one↵2: two↵3: three↵4: four↵5: five"'); - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_errorhandling_uispec.js b/test/editor/specs/scenario/cookbook_errorhandling_uispec.js deleted file mode 100644 index 5285c5c0f..000000000 --- a/test/editor/specs/scenario/cookbook_errorhandling_uispec.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('messages', function () { - it('trigger a flow when a node throws an error', function () { - var injectNode = workspace.addNode('inject'); - var functionNode = workspace.addNode('function'); - var catchNode = workspace.addNode('catch', 0 , 80); - var debugNode = workspace.addNode('debug'); - - functionNode.edit(); - functionNode.setFunction('node.error("an example error", msg);'); - functionNode.clickOk(); - - catchNode.edit(); - catchNode.setScope(functionNode); - catchNode.clickOk(); - - debugNode.edit(); - debugNode.setOutput('error'); - debugNode.clickOk(); - - injectNode.connect(functionNode); - catchNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql(['"an example error"', 'function']); - }); - - // skip this case since the flow outputs random results. - it.skip('automatically retry an action after an error'); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_flowcontrol_uispec.js b/test/editor/specs/scenario/cookbook_flowcontrol_uispec.js deleted file mode 100644 index 724d1c56f..000000000 --- a/test/editor/specs/scenario/cookbook_flowcontrol_uispec.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('flow control', function () { - it('trigger a flow whenever Node-RED starts', function () { - var injectNode = workspace.addNode('inject'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'Started!'); - injectNode.setOnce(true); - injectNode.clickOk(); - injectNode.connect(debugNode); - - debugTab.open(); - workspace.deploy(); - debugTab.getMessage().should.eql('"Started!"'); - }); - - it('trigger a flow at regular intervals', function () { - var injectNode = workspace.addNode('inject'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setRepeat('interval'); - injectNode.setRepeatInterval(1); - injectNode.clickOk(); - injectNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - specUtil.pause(1000); - var t1 = Number(debugTab.getMessage(1)); - t1.should.within(1500000000000, 3000000000000); - specUtil.pause(1000); - debugTab.getMessage(2).should.within(t1 + 900, 3000000000000); - }); - - // skip this case since it needs up to one minite. - it.skip('trigger a flow at a specific time'); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_httpendpoints_uispec.js b/test/editor/specs/scenario/cookbook_httpendpoints_uispec.js deleted file mode 100644 index 134498370..000000000 --- a/test/editor/specs/scenario/cookbook_httpendpoints_uispec.js +++ /dev/null @@ -1,572 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var helper = require("../../editor_helper"); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); - -var httpNodeRoot = "/api"; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('HTTP endpoints', function () { - it('create an HTTP endpoint', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Hello World!

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello World!').should.not.eql(-1); - }); - - it('handle query parameters passed to an HTTP endpoint', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-query"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Hello {{req.query.name}}!

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-query?name=Nick'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('handle url parameters in an HTTP endpoint', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-param/:name"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Hello {{req.params.name}}!

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-param/Dave'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Dave!').should.not.eql(-1); - }); - - it('access HTTP request headers', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-headers"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    User agent: {{req.headers.user-agent}}

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 100); - var changeNode = workspace.addNode("change"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", '{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}', "json"); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-headers'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Mozilla').should.not.eql(-1); - }); - - it('include data captured in another flow', function () { - var injectNodeTimestamp = workspace.addNode("inject"); - var changeNodeStore = workspace.addNode("change"); - - var httpInNode = workspace.addNode("httpIn", 0, 100); - var changeNodeCopy = workspace.addNode("change"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - injectNodeTimestamp.edit(); - injectNodeTimestamp.setPayload("date"); - injectNodeTimestamp.clickOk(); - - changeNodeStore.edit(); - changeNodeStore.ruleSet("timestamp", "flow", "payload", "msg"); - changeNodeStore.clickOk(); - - injectNodeTimestamp.connect(changeNodeStore); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-data"); - httpInNode.clickOk(); - - changeNodeCopy.edit(); - changeNodeCopy.ruleSet("timestamp", "msg", "timestamp", "flow"); - changeNodeCopy.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Time: {{ timestamp }}

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(changeNodeCopy); - changeNodeCopy.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNodeCheck = workspace.addNode("inject", 0, 300); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setMethod("GET"); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-data'); - httpRequestNode.clickOk(); - - injectNodeCheck.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNodeTimestamp.clickLeftButton(); - injectNodeCheck.clickLeftButton(); - var index = debugTab.getMessage().indexOf('Time: ') + 6; - debugTab.getMessage().substring(index, index + 13).should.within(1500000000000, 3000000000000); - }); - - it('serve JSON content', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var changeNode = workspace.addNode("change"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-json"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate('{ "Hello": "World" }'); - templateNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", "{}", "json", "1"); - changeNode.addRule(); - changeNode.ruleSet("headers.content-type", "msg", "application/json", "str", "2"); - changeNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(changeNode); - changeNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 200); - var httpRequestNode = workspace.addNode("httpRequest"); - var changeNodeCheck = workspace.addNode("change"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setMethod("GET"); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-json'); - httpRequestNode.clickOk(); - - changeNodeCheck.edit(); - changeNodeCheck.ruleSet("payload", "msg", "headers.content-type", "msg", "1"); - changeNodeCheck.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(changeNodeCheck); - changeNodeCheck.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - var messages = debugTab.getMessage(); - messages.indexOf('application/json').should.not.eql(-1); - }); - - it('serve a local file', function () { - var httpInNode = workspace.addNode("httpIn"); - var fileInNode = workspace.addNode("fileIn"); - var changeNode = workspace.addNode("change", 200, 100); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("get"); - httpInNode.setUrl("/hello-file"); - httpInNode.clickOk(); - - fileInNode.edit(); - fileInNode.setFilename("test/resources/file-in-node/test.txt"); - fileInNode.setOutput(""); - fileInNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", "{}", "json"); - changeNode.addRule(); - changeNode.ruleSet("headers.content-type", "msg", "text/plain", "str", "2"); - changeNode.clickOk(); - - httpInNode.connect(fileInNode); - fileInNode.connect(changeNode); - changeNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 200); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-file'); - httpRequestNode.setMethod("GET"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Text file').should.not.eql(-1); - }); - - it('post raw data to a flow', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("post"); - httpInNode.setUrl("/hello-raw"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Hello {{ payload }}!

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("str", "Nick"); - injectNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-raw'); - httpRequestNode.setMethod("POST"); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('post form data to a flow', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("post"); - httpInNode.setUrl("/hello-form"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Hello {{ payload.name }}!

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 100); - var changeNode = workspace.addNode("change"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("str", "name=Nick"); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", '{"content-type":"application/x-www-form-urlencoded"}', "json"); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-form'); - httpRequestNode.setMethod("POST"); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('post JSON data to a flow', function () { - var httpInNode = workspace.addNode("httpIn"); - var templateNode = workspace.addNode("template"); - var httpResponseNode = workspace.addNode("httpResponse"); - - httpInNode.edit(); - httpInNode.setMethod("post"); - httpInNode.setUrl("/hello-json"); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate("\n\n\n

    Hello {{ payload.name }}!

    \n\n"); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - - // The code for confirmation starts from here. - var injectNode = workspace.addNode("inject", 0, 100); - var changeNode = workspace.addNode("change"); - var httpRequestNode = workspace.addNode("httpRequest"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("json", '{"name":"Nick"}'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("headers", "msg", '{"content-type":"application/json"}', "json"); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-json'); - httpRequestNode.setMethod("POST"); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().indexOf('Hello Nick!').should.not.eql(-1); - }); - - it('work with cookies', function () { - var httpInNodeFormat = workspace.addNode("httpIn"); - var functionNodeFormat = workspace.addNode("function", 240); - var templateNode = workspace.addNode("template", 400); - var httpResponseNode = workspace.addNode("httpResponse", 600); - - var httpInNodeAdd = workspace.addNode("httpIn", 0, 100); - var functionNodeAdd = workspace.addNode("function", 240); - var changeNode = workspace.addNode("change", 400); - - var httpInNodeClear = workspace.addNode("httpIn", 0, 200); - var functionNodeClear = workspace.addNode("function", 250); - - httpInNodeFormat.edit(); - httpInNodeFormat.setMethod("get"); - httpInNodeFormat.setUrl("/hello-cookie"); - httpInNodeFormat.clickOk(); - - functionNodeFormat.edit(); - functionNodeFormat.setFunction("msg.payload = JSON.stringify(msg.req.cookies,null,4);\nreturn msg;"); - functionNodeFormat.clickOk(); - - templateNode.edit(); - templateNode.setSyntax("mustache"); - templateNode.setFormat("handlebars"); - templateNode.setTemplate('\n\n\n

    Cookies

    \n

    Add a cookieClear cookies

    \n
    {{ payload }}
    \n\n'); - templateNode.clickOk(); - - httpInNodeFormat.connect(functionNodeFormat); - functionNodeFormat.connect(templateNode); - templateNode.connect(httpResponseNode); - - httpInNodeAdd.edit(); - httpInNodeAdd.setMethod("get"); - httpInNodeAdd.setUrl("/hello-cookie/add"); - httpInNodeAdd.clickOk(); - - functionNodeAdd.edit(); - functionNodeAdd.setFunction('msg.cookies = { };\n msg.cookies["demo-"+(Math.floor(Math.random()*1000))] = Date.now();\nreturn msg;'); - functionNodeAdd.clickOk(); - - changeNode.edit(); - changeNode.ruleSet("statusCode", "msg", "302", "num"); - changeNode.addRule(); - changeNode.ruleSet("headers", "msg", "{}", "json", "2"); - changeNode.addRule(); - changeNode.ruleSet("headers.location", "msg", httpNodeRoot + "/hello-cookie", "str", "3"); - changeNode.clickOk(); - - httpInNodeAdd.connect(functionNodeAdd); - functionNodeAdd.connect(changeNode); - changeNode.connect(httpResponseNode); - - httpInNodeClear.edit(); - httpInNodeClear.setMethod("get"); - httpInNodeClear.setUrl("/hello-cookie/clear"); - httpInNodeClear.clickOk(); - - functionNodeClear.edit(); - functionNodeClear.setFunction("var cookieNames = Object.keys(msg.req.cookies).filter(function(cookieName) { return /^demo-/.test(cookieName);});\nmsg.cookies = {};\n\ncookieNames.forEach(function(cookieName) {\n msg.cookies[cookieName] = null;\n});\nreturn msg;\n"); - functionNodeClear.clickOk(); - - httpInNodeClear.connect(functionNodeClear); - functionNodeClear.connect(changeNode); - - workspace.deploy(); - // This case cannot be checked since http request node does not transfer cookies when redirected. - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_httprequests_uispec.js b/test/editor/specs/scenario/cookbook_httprequests_uispec.js deleted file mode 100644 index 797b06041..000000000 --- a/test/editor/specs/scenario/cookbook_httprequests_uispec.js +++ /dev/null @@ -1,300 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('HTTP requests', function () { - it('simple get request', function () { - var injectNode = workspace.addNode('inject'); - var httpRequestNode = workspace.addNode('httpRequest'); - var htmlNode = workspace.addNode('html'); - var debugNode = workspace.addNode('debug'); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - httpRequestNode.setUrl(helper.url()); - httpRequestNode.clickOk(); - - htmlNode.edit(); - htmlNode.setSelector('title'); - htmlNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(htmlNode); - htmlNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Node-RED"'); - }); - - it('set the URL of a request', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', helper.url()); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('url', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.containEql('Node-RED'); - }); - - it('set the URL of a request using a template', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'settings'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('query', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + '/{{{query}}}'); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.containEql('httpNodeRoot'); - }); - - it('set the query string parameters', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'Nick'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleSet('query', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/set-query?q={{{query}}}'); - httpRequestNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - // The code for confirmation starts from here. - var httpInNode = workspace.addNode('httpIn', 0, 200); - var templateNode = workspace.addNode('template'); - var httpResponseNode = workspace.addNode('httpResponse'); - - httpInNode.edit(); - httpInNode.setMethod('get'); - httpInNode.setUrl('/set-query'); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax('mustache'); - templateNode.setFormat('handlebars'); - templateNode.setTemplate('Hello {{req.query.q}}'); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello Nick"'); - }); - - it('get a parsed JSON response', function () { - var injectNode = workspace.addNode('inject'); - var changeNodeSetPost = workspace.addNode('change'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setPayload('str', 'json-response'); - injectNode.clickOk(); - - changeNodeSetPost.edit(); - changeNodeSetPost.ruleSet('post', 'msg', 'payload', 'msg'); - changeNodeSetPost.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - var url = helper.url() + httpNodeRoot + '/{{post}}'; - httpRequestNode.setUrl(url); - httpRequestNode.setReturn('obj'); - httpRequestNode.clickOk(); - - debugNode.edit(); - debugNode.setOutput('payload.title'); - debugNode.clickOk(); - - injectNode.connect(changeNodeSetPost); - changeNodeSetPost.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - // The code for confirmation starts from here. - var httpInNode = workspace.addNode('httpIn', 0, 200); - var templateNode = workspace.addNode('template'); - var changeNodeSetHeader = workspace.addNode('change'); - var httpResponseNode = workspace.addNode('httpResponse'); - - httpInNode.edit(); - httpInNode.setMethod('get'); - httpInNode.setUrl('/json-response'); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax('mustache'); - templateNode.setFormat('handlebars'); - templateNode.setTemplate('{"title": "Hello"}'); - templateNode.clickOk(); - - changeNodeSetHeader.edit(); - changeNodeSetHeader.ruleSet('headers', 'msg', '{"content-type":"application/json"}', 'json'); - changeNodeSetHeader.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(changeNodeSetHeader); - changeNodeSetHeader.connect(httpResponseNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello"'); - }); - - it('get a binary response', function () { - var injectNode = workspace.addNode('inject'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - httpRequestNode.edit(); - httpRequestNode.setMethod('GET'); - httpRequestNode.setUrl(helper.url() + '/settings'); - httpRequestNode.setReturn('bin'); - httpRequestNode.clickOk(); - - injectNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - - debugTab.getMessage().should.eql(['123', '34', '104', '116', '116', '112', '78', '111', '100', '101']); - }); - - it('set a request header', function () { - var injectNode = workspace.addNode('inject'); - var functionNode = workspace.addNode('function'); - var httpRequestNode = workspace.addNode('httpRequest'); - var debugNode = workspace.addNode('debug'); - - functionNode.edit(); - functionNode.setFunction('msg.payload = "data to post";\nreturn msg;'); - functionNode.clickOk(); - - httpRequestNode.edit(); - httpRequestNode.setMethod('POST'); - var url = helper.url() + httpNodeRoot + '/set-header'; - httpRequestNode.setUrl(url); - httpRequestNode.clickOk(); - - injectNode.connect(functionNode); - functionNode.connect(httpRequestNode); - httpRequestNode.connect(debugNode); - - // The code for confirmation starts from here. - var httpInNode = workspace.addNode('httpIn', 0, 200); - var templateNode = workspace.addNode('template'); - var httpResponseNode = workspace.addNode('httpResponse'); - - httpInNode.edit(); - httpInNode.setMethod('post'); - httpInNode.setUrl('/set-header'); - httpInNode.clickOk(); - - templateNode.edit(); - templateNode.setSyntax('mustache'); - templateNode.setFormat('handlebars'); - templateNode.setTemplate('{{ payload }}'); - templateNode.clickOk(); - - httpInNode.connect(templateNode); - templateNode.connect(httpResponseNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"data to post"'); - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_messages_uispec.js b/test/editor/specs/scenario/cookbook_messages_uispec.js deleted file mode 100644 index 78facbcda..000000000 --- a/test/editor/specs/scenario/cookbook_messages_uispec.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require('should'); -var fs = require('fs-extra'); - -var helper = require('../../editor_helper'); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = '/api'; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - helper.startServer(); - }); - - after(function () { - helper.stopServer(); - }); - - describe('messages', function () { - it('set a message property to a fixed value', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var debugNode = workspace.addNode('debug'); - - changeNode.edit(); - changeNode.ruleSet('payload', 'msg', 'Hello World!'); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello World!"'); - }); - - it('delete a message property', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var debugNode = workspace.addNode('debug'); - - changeNode.edit(); - changeNode.ruleDelete(); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('undefined'); - }); - - it('move a message property', function () { - var injectNode = workspace.addNode('inject'); - var changeNode = workspace.addNode('change'); - var debugNode = workspace.addNode('debug'); - - injectNode.edit(); - injectNode.setTopic('Hello'); - injectNode.clickOk(); - - changeNode.edit(); - changeNode.ruleMove('topic', 'msg', 'payload', 'msg'); - changeNode.clickOk(); - - injectNode.connect(changeNode); - changeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"Hello"'); - }); - - it('map a property between different numeric ranges', function () { - var injectNode1 = workspace.addNode('inject'); - var injectNode2 = workspace.addNode('inject', 0, 100); - var injectNode3 = workspace.addNode('inject', 0, 200); - var rangeNode = workspace.addNode('range', 200, 100); - var debugNode = workspace.addNode('debug', 400); - - injectNode1.edit(); - injectNode1.setPayload('num', 0); - injectNode1.clickOk(); - injectNode2.edit(); - injectNode2.setPayload('num', 512); - injectNode2.clickOk(); - injectNode3.edit(); - injectNode3.setPayload('num', 1023); - injectNode3.clickOk(); - - rangeNode.edit(); - rangeNode.setAction('clamp'); - rangeNode.setRange(0, 1023, 0, 5); - rangeNode.clickOk(); - - injectNode1.connect(rangeNode); - injectNode2.connect(rangeNode); - injectNode3.connect(rangeNode); - rangeNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode1.clickLeftButton(); - debugTab.getMessage(1).should.eql('0'); - injectNode2.clickLeftButton(); - debugTab.getMessage(2).should.eql('2.5024437927663734'); - injectNode3.clickLeftButton(); - debugTab.getMessage(3).should.eql('5'); - }); - }); -}); diff --git a/test/editor/specs/scenario/cookbook_mqtt_uispec.js b/test/editor/specs/scenario/cookbook_mqtt_uispec.js deleted file mode 100644 index b68170eb5..000000000 --- a/test/editor/specs/scenario/cookbook_mqtt_uispec.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require('when'); -var should = require("should"); -var fs = require('fs-extra'); - -var helper = require("../../editor_helper"); -var debugTab = require('../../pageobjects/editor/debugTab_page'); -var workspace = require('../../pageobjects/editor/workspace_page'); -var specUtil = require('../../pageobjects/util/spec_util_page'); - -var httpNodeRoot = "/api"; - -var mqttServer; -var mosca = require('mosca'); -var moscaSettings = { - port: parseInt(Math.random() * 16383 + 49152), - persistence: { - // Needs for retaining messages. - factory: mosca.persistence.Memory - } -}; - -// https://cookbook.nodered.org/ -describe('cookbook', function () { - beforeEach(function () { - workspace.init(); - }); - - before(function () { - browser.call(function () { - return new Promise(function (resolve, reject) { - mqttServer = new mosca.Server(moscaSettings, function (err) { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - }); - helper.startServer(); - }); - - after(function () { - browser.call(function () { - return new Promise(function (resolve, reject) { - mqttServer.close(function () { - resolve(); - }); - }); - }); - helper.stopServer(); - }); - - describe('MQTT', function () { - it('Add an MQTT broker to prepare for UI test', function () { - var mqttOutNode = workspace.addNode("mqttOut"); - - mqttOutNode.edit(); - mqttOutNode.mqttBrokerNode.edit(); - mqttOutNode.mqttBrokerNode.setServer("localhost", moscaSettings.port); - mqttOutNode.mqttBrokerNode.clickOk(); - mqttOutNode.clickOk(); - - workspace.deploy(); - }); - - it('Connect to an MQTT broker', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("num", 22); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.setTopic("sensors/livingroom/temp"); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/livingroom/temp"); - mqttInNode.setQoS("2"); - mqttInNode.clickOk(); - - mqttInNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"22"'); - }); - - // skip this case since it is same as other cases. - it.skip('Publish messages to a topic'); - - it('Set the topic of a published message', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - injectNode.edit(); - injectNode.setPayload("num", 22); - injectNode.setTopic("sensors/kitchen/temperature"); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - // The code for confirmation starts from here. - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var debugNode = workspace.addNode("debug"); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/kitchen/temperature"); - mqttInNode.clickOk(); - - mqttInNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql('"22"'); - }); - - it('Publish a retained message to a topic', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - injectNode.edit(); - injectNode.setPayload("num", 22); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.setTopic("sensors/livingroom/temp"); - mqttOutNode.setRetain("true"); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - workspace.deploy(); - debugTab.open(); - injectNode.clickLeftButton(); - - // The code for confirmation starts from here. - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var debugNode = workspace.addNode("debug"); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/livingroom/temp"); - mqttInNode.clickOk(); - - mqttInNode.connect(debugNode); - // The code for confirmation ends here. - - workspace.deploy(); - debugTab.open(true); - debugTab.getMessage().should.eql('"22"'); - }); - - // skip this case since it is same as other cases. - it.skip('Subscribe to a topic'); - - it('Receive a parsed JSON message', function () { - var injectNode = workspace.addNode("inject"); - var mqttOutNode = workspace.addNode("mqttOut"); - - var mqttInNode = workspace.addNode("mqttIn", 0, 100); - var jsonNode = workspace.addNode("json"); - var debugNode = workspace.addNode("debug"); - - injectNode.edit(); - injectNode.setPayload("json", '{"sensor_id": 1234, "temperature": 13 }'); - injectNode.clickOk(); - - mqttOutNode.edit(); - mqttOutNode.setTopic("sensors/livingroom/temp"); - mqttOutNode.clickOk(); - - injectNode.connect(mqttOutNode); - - mqttInNode.edit(); - mqttInNode.setTopic("sensors/#"); - mqttInNode.setQoS("2"); - mqttInNode.clickOk(); - - jsonNode.edit(); - jsonNode.setProperty("payload"); - jsonNode.clickOk(); - - mqttInNode.connect(jsonNode); - jsonNode.connect(debugNode); - - workspace.deploy(); - - debugTab.open(); - injectNode.clickLeftButton(); - debugTab.getMessage().should.eql(['1234', '13']); - }); - }); -}); diff --git a/test/editor/wdio.conf.js b/test/editor/wdio.conf.js deleted file mode 100644 index 4e5a602e0..000000000 --- a/test/editor/wdio.conf.js +++ /dev/null @@ -1,338 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var browserstack = require('browserstack-local'); -exports.config = { - - // - // ================== - // Specify Test Files - // ================== - // Define which test specs should run. The pattern is relative to the directory - // from which `wdio` was called. Notice that, if you are calling `wdio` from an - // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working - // directory is where your package.json resides, so `wdio` will be called from there. - // - specs: [ - './test/editor/**/*_uispec.js' - ], - // Patterns to exclude. - exclude: [ - // 'path/to/excluded/files' - ], - // - // ============ - // Capabilities - // ============ - // Define your capabilities here. WebdriverIO can run multiple capabilities at the same - // time. Depending on the number of capabilities, WebdriverIO launches several test - // sessions. Within your capabilities you can overwrite the spec and exclude options in - // order to group specific specs to a specific capability. - // - // First, you can define how many instances should be started at the same time. Let's - // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have - // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec - // files and you set maxInstances to 10, all spec files will get tested at the same time - // and 30 processes will get spawned. The property handles how many capabilities - // from the same test should run tests. - // - // maxInstances: 10, - // - // If you have trouble getting all important capabilities together, check out the - // Sauce Labs platform configurator - a great tool to configure your capabilities: - // https://docs.saucelabs.com/reference/platforms-configurator - // - // capabilities: [{ - // maxInstances can get overwritten per capability. So if you have an in-house Selenium - // grid with only 5 firefox instances available you can make sure that not more than - // 5 instances get started at a time. - // maxInstances: 5, - // - // browserName: 'firefox' - // }], - // - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // - // By default WebdriverIO commands are executed in a synchronous way using - // the wdio-sync package. If you still want to run your tests in an async way - // e.g. using promises you can set the sync option to false. - sync: true, - // - // Level of logging verbosity: silent | verbose | command | data | result | error - logLevel: 'silent', - // - // Enables colors for log output. - coloredLogs: true, - // - // Warns when a deprecated command is used - deprecationWarnings: false, - // - // If you only want to run your tests until a specific amount of tests have failed use - // bail (default is 0 - don't bail, run all tests). - bail: 0, - // - // Saves a screenshot to a given path if a command fails. - screenshotPath: './test/errorShots/', - // - // Set a base URL in order to shorten url command calls. If your `url` parameter starts - // with `/`, the base url gets prepended, not including the path portion of your baseUrl. - // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url - // gets prepended directly. - baseUrl: 'http://localhost', - // - // Default timeout for all waitFor* commands. - waitforTimeout: 20000, - // - // Default timeout in milliseconds for request - // if Selenium Grid doesn't send response - connectionRetryTimeout: 90000, - // - // Default request retries count - connectionRetryCount: 3, - // - // Initialize the browser instance with a WebdriverIO plugin. The object should have the - // plugin name as key and the desired plugin options as properties. Make sure you have - // the plugin installed before running any tests. The following plugins are currently - // available: - // WebdriverCSS: https://github.com/webdriverio/webdrivercss - // WebdriverRTC: https://github.com/webdriverio/webdriverrtc - // Browserevent: https://github.com/webdriverio/browserevent - // plugins: { - // webdrivercss: { - // screenshotRoot: 'my-shots', - // failedComparisonsRoot: 'diffs', - // misMatchTolerance: 0.05, - // screenWidth: [320,480,640,1024] - // }, - // webdriverrtc: {}, - // browserevent: {} - // }, - // - // Test runner services - // Services take over a specific job you don't want to take care of. They enhance - // your test setup with almost no effort. Unlike plugins, they don't add new - // commands. Instead, they hook themselves up into the test process. - //services: ['chromedriver'], - // - // Framework you want to run your specs with. - // The following are supported: Mocha, Jasmine, and Cucumber - // see also: http://webdriver.io/guide/testrunner/frameworks.html - // - // Make sure you have the wdio adapter package for the specific framework installed - // before running any tests. - framework: 'mocha', - // - // Test reporter for stdout. - // The only one supported by default is 'dot' - // see also: http://webdriver.io/guide/reporters/dot.html - reporters: ['spec'], - - // - // Options to be passed to Mocha. - // See the full list at http://mochajs.org/ - mochaOpts: { - timeout: 1000000, - ui: 'bdd' - }, - // - // ===== - // Hooks - // ===== - // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance - // it and to build services around it. You can either apply a single function or an array of - // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got - // resolved to continue. - /** - * Gets executed once before all workers get launched. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - onPrepare: function (config, capabilities) { - if (process.env.BROWSERSTACK) { - return new Promise(function (resolve, reject) { - var options = { key: exports.config.key }; - var proxy = process.env.http_proxy || process.env.HTTP_PROXY; - if (proxy) { - var proxyConfigs = proxy.match(/^(https?):\/\/(([^:@\/]+):([^:@\/]+)@)?([^:@\/]+)(:([^:@\/]+))?\/?$/); - if (proxyConfigs) { - var protocol = proxyConfigs[1]; - var user = proxyConfigs[3]; - var pass = proxyConfigs[4]; - var host = proxyConfigs[5]; - var port = proxyConfigs[7]; - if (!port) { - if (protocol === 'http') { - port = 80; - } else if (protocol === 'https') { - port = 443; - } - } - if (host) { options.proxyHost = host; } - if (port) { options.proxyPort = port; } - if (user) { options.proxyUser = user; } - if (pass) { options.proxyPass = pass; } - } else { - reject('error in parsing the environment variable, http_proxy'); - } - } - exports.bs_local = new browserstack.Local(); - exports.bs_local.start(options, function (error) { - if (error) { - return reject(error); - } - resolve(); - }); - }); - } - }, - /** - * Gets executed just before initialising the webdriver session and test framework. It allows you - * to manipulate configurations depending on the capability or spec. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // beforeSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed before test execution begins. At this point you can access to all global - * variables like `browser`. It is the perfect place to define custom commands. - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // before: function (capabilities, specs) { - // }, - /** - * Runs before a WebdriverIO command gets executed. - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - */ - // beforeCommand: function (commandName, args) { - // }, - - /** - * Hook that gets executed before the suite starts - * @param {Object} suite suite details - */ - // beforeSuite: function (suite) { - // }, - /** - * Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. - * @param {Object} test test details - */ - // beforeTest: function (test) { - // }, - /** - * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling - * beforeEach in Mocha) - */ - // beforeHook: function () { - // }, - /** - * Hook that gets executed _after_ a hook within the suite ends (e.g. runs after calling - * afterEach in Mocha) - */ - // afterHook: function () { - // }, - /** - * Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) ends. - * @param {Object} test test details - */ - // afterTest: function (test) { - // }, - /** - * Hook that gets executed after the suite has ended - * @param {Object} suite suite details - */ - // afterSuite: function (suite) { - // }, - - /** - * Runs after a WebdriverIO command gets executed - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - * @param {Number} result 0 - command success, 1 - command error - * @param {Object} error error object if any - */ - // afterCommand: function (commandName, args, result, error) { - // }, - /** - * Gets executed after all tests are done. You still have access to all global variables from - * the test. - * @param {Number} result 0 - test pass, 1 - test fail - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // after: function (result, capabilities, specs) { - // }, - /** - * Gets executed right after terminating the webdriver session. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // afterSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed after all workers got shut down and the process is about to exit. - * @param {Object} exitCode 0 - success, 1 - fail - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - onComplete: function(exitCode, config, capabilities) { - if (process.env.BROWSERSTACK) { - exports.bs_local.stop(function () {}); - } - } -}; - -if (process.env.BROWSERSTACK) { - exports.config.maxInstances = 1; - if (process.env.BROWSERSTACK_USERNAME && process.env.BROWSERSTACK_ACCESS_KEY) { - exports.config.user = process.env.BROWSERSTACK_USERNAME; - exports.config.key = process.env.BROWSERSTACK_ACCESS_KEY; - } else { - console.log('You need to set the following environment variables.'); - console.log('BROWSERSTACK_USERNAME='); - console.log('BROWSERSTACK_ACCESS_KEY='); - } - exports.config.services = ['browserstack']; - var capabilities = []; - capabilities.push({ os: 'Windows', os_version: '10', browser: 'Chrome', resolution: '1920x1080', 'browserstack.local': true }); - capabilities.push({ os: 'Windows', os_version: '10', browser: 'Firefox', resolution: '1920x1080', 'browserstack.local': true }); - capabilities.push({ os: 'OS X', os_version: 'Catalina', browser: 'Chrome', resolution: '1920x1080', 'browserstack.local': true }); - capabilities.push({ os: 'OS X', os_version: 'Catalina', browser: 'Firefox', resolution: '1920x1080', 'browserstack.local': true }); - exports.config.capabilities = capabilities; -} else { - exports.config.maxInstances = 10; - exports.config.port = 9515; - exports.config.path = '/'; - exports.config.services = ['chromedriver']; - exports.config.capabilities = [{ - maxInstances: 2, - browserName: 'chrome', - 'goog:chromeOptions': { - args: process.env.NODE_RED_NON_HEADLESS - // Runs tests with opening a browser. - ? ['--disable-gpu', '--no-sandbox'] - // Runs tests without opening a browser. - : ['--headless', '--disable-gpu', 'window-size=1920,1080', '--no-sandbox'] - } - }]; -} diff --git a/test/node_modules/nr-test-utils/index.js b/test/node_modules/nr-test-utils/index.js deleted file mode 100644 index 56bc369e6..000000000 --- a/test/node_modules/nr-test-utils/index.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -const path = require("path"); -const fs = require("fs"); - -const PACKAGE_ROOT = "../../../packages/node_modules"; - -module.exports = { - require: function(file) { - // console.log(path.join(__dirname,PACKAGE_ROOT,file)) - return require(path.join(PACKAGE_ROOT,file)); - }, - resolve: function(file) { - return path.resolve(path.join(__dirname,PACKAGE_ROOT,file)); - } -} diff --git a/test/node_modules/nr-test-utils/package.json b/test/node_modules/nr-test-utils/package.json deleted file mode 100644 index ee32d2320..000000000 --- a/test/node_modules/nr-test-utils/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "nr-test-utils", - "version": "0.20.0", - "license": "Apache-2.0", - "private": true -} diff --git a/test/nodes/core/common/20-inject_spec.js b/test/nodes/core/common/20-inject_spec.js deleted file mode 100644 index f760bb646..000000000 --- a/test/nodes/core/common/20-inject_spec.js +++ /dev/null @@ -1,669 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var injectNode = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); - -describe('inject node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - helper.stopServer(done); - }); - }); - - function basicTest(type, val, rval) { - it('inject value ('+type+')', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: val, payloadType: type, wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - if (rval) { - msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); - } - else { - msg.should.have.property("payload", val); - } - done(); - } catch (err) { - done(err); - } - }); - n1.receive({}); - }); - }); - } - - basicTest("num", 10); - basicTest("str", "10"); - basicTest("bool", true); - var val_json = '{ "x":"vx", "y":"vy", "z":"vz" }'; - basicTest("json", val_json, JSON.parse(val_json)); - var val_buf = "[1,2,3,4,5]"; - basicTest("bin", val_buf, Buffer.from(JSON.parse(val_buf))); - - it('inject value of environment variable ', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "NR_TEST", payloadType: "env", wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "foo"); - done(); - } catch (err) { - done(err); - } - }); - process.env.NR_TEST = 'foo'; - n1.receive({}); - }); - }); - - it('sets the value of flow context property', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "changeMe"); - done(); - } catch (err) { - done(err); - } - }); - n1.context().flow.set("flowValue", "changeMe"); - n1.receive({}); - }); - }); - - it('sets the value of persistable flow context property', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "#:(memory0)::flowValue", payloadType: "flow", wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "changeMe"); - done(); - } catch (err) { - done(err); - } - }); - n1.context().flow.set("flowValue", "changeMe", "memory0", function (err) { - n1.receive({}); - }); - }); - }); - }); - - it('sets the value of two persistable flow context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "flow", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var global = n0.context().flow; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - - it('sets the value of global context property', function (done) { - var flow = [{id: "n1", type: "inject", topic: "t1", payload: "globalValue", payloadType: "global", wires: [["n2"]]}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "changeMe"); - done(); - } catch (err) { - done(err); - } - }); - n1.context().global.set("globalValue", "changeMe"); - n1.receive({}); - }); - }); - - it('sets the value of persistable global context property', function (done) { - var flow = [{id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "foo"); - done(); - } catch (err) { - done(err); - } - }); - var global = n1.context().global; - global.set("val", "foo", "memory1", function (err) { - n1.receive({}); - }); - }); - }); - }); - - it('sets the value of two persistable global context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var global = n0.context().global; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - - it('sets the value of persistable flow & global context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "flow", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var context = n0.context(); - var flow = context.flow; - var global = context.global; - flow.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - - it('sets the value of two persistable global context property', function (done) { - var flow = [{id: "n0", z: "flow", type: "inject", topic: "t0", payload: "#:(memory0)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n1", z: "flow", type: "inject", topic: "t1", payload: "#:(memory1)::val", payloadType: "global", wires: [["n2"]]}, - {id: "n2", z: "flow", type: "helper"}]; - helper.load(injectNode, flow, function () { - initContext(function () { - var n0 = helper.getNode("n0"); - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic"); - if (msg.topic === "t0") { - msg.should.have.property("payload", "foo"); - } - else if (msg.topic === "t1") { - msg.should.have.property("payload", "bar"); - } - else { - done(new Error("unexpected message")); - } - count++; - if (count === 2) { - done(); - } - } catch (err) { - done(err); - } - }); - var global = n0.context().global; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - n0.receive({}); - n1.receive({}); - }); - }); - }); - }); - }); - it('should inject once with default delay property', function(done) { - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('onceDelay', 100); - done(); - }); - }); - - it('should inject once with default delay', function(done) { - var timestamp = new Date(); - timestamp.setSeconds(timestamp.getSeconds() + 1); - - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 't1'); - msg.should.have.property('payload'); - should(msg.payload).be.lessThan(timestamp.getTime()); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should inject once with 500 msec. delay', function(done) { - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, onceDelay: 0.5, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('onceDelay', 500); - done(); - }); - }); - - it('should inject once with delay of two seconds', function(done) { - this.timeout(2700); // have to wait for the inject with delay of two seconds - - var timestamp = new Date(); - timestamp.setSeconds(timestamp.getSeconds() + 1); - - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", - once: true, onceDelay: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 't1'); - should(msg.payload).be.greaterThan(timestamp.getTime()); - done(); - }); - }); - }); - - it('should inject repeatedly', function(done) { - - helper.load(injectNode, [{id:"n1", type:"inject", - payload:"payload", topic: "t2", - repeat: 0.2, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property('topic', 't2'); - msg.should.have.property('payload', 'payload'); - count += 1; - if (count > 2) { - helper.clearFlows().then(function() { - done(); - }); - } - }); - }); - }); - - it('should inject once with delay of two seconds and repeatedly', function(done) { - var timestamp = new Date(); - timestamp.setSeconds(timestamp.getSeconds() + 1); - - helper.load(injectNode, [{id:"n1", type:"inject", topic: "t1", - payload:"",payloadType:"date", repeat: 0.2, - once: true, onceDelay: 1.2, wires:[["n2"]] }, - {id:"n2", type:"helper"}], - function() { - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property('topic', 't1'); - should(msg.payload).be.greaterThan(timestamp.getTime()); - count += 1; - if (count > 2) { - helper.clearFlows().then(function() { - done(); - }); - } - }); - }); - }); - - it('should inject with cron', function(done) { - helper.load(injectNode, [{id:"n1", type:"inject", - payloadType:"date", topic: "t3", - crontab: "* * * * * *", wires:[["n3"]] }, - {id:"n3", type:"helper"}], - function() { - var n3 = helper.getNode("n3"); - n3.on("input", function(msg) { - msg.should.have.property('topic', 't3'); - msg.should.have.property('payload').be.a.Number(); - helper.clearFlows().then(function() { - done(); - }); - }); - }); - }); - - - it('should inject multiple properties ', function (done) { - var flow = [{id: "n1", type: "inject", props: [{p:"topic", v:"t1", vt:"str"}, {p:"payload", v:"foo", vt:"str"}, {p:"x", v: 10, "vt":"num"}, {p:"y", v: "x+2", "vt":"jsonata"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.have.property("payload", "foo"); - msg.should.have.property("x", 10); - msg.should.have.property("y", 12); - done(); - } catch (err) { - done(err); - } - }); - n1.receive({}); - }); - }); - - - it('should inject custom properties in message', function (done) { - //n1: inject node with { topic:"static", payload:"static", bool1:true, str1:"1" } - var flow = [{id: "n1", type: "inject", props: [{p:"payload", v:"static", vt:"str"}, {p:"topic", v:"static", vt:"str"}, {p:"bool1", v:"true", vt:"bool"}, {p:"str1", v:"1", vt:"str"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.not.have.property("payload"); //payload removed - msg.should.have.property("topic", "t_override"); //changed value to t_override - msg.should.have.property("str1", 1);//changed type from str to num - msg.should.have.property("num1", 1);//new prop - msg.should.have.property("bool1", false);//changed value to false - done(); - } catch (err) { - done(err); - } - }); - n1.receive({ __user_inject_props__: [ - {p:"topic", v:"t_override", vt:"str"}, //change value to t_override - {p:"str1", v:"1", vt:"num"}, //change type - {p:"num1", v:"1", vt:"num"}, //new prop - {p:"bool1", v:"false", vt:"bool"}, //change value to false - ]}); - }); - }); - - - it('should inject multiple properties using legacy props if needed', function (done) { - var flow = [{id: "n1", type: "inject", payload:"123", payloadType:"num", topic:"foo", props: [{p:"topic", vt:"str"}, {p:"payload"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "foo"); - msg.should.have.property("payload", 123); - done(); - } catch (err) { - done(err); - } - }); - n1.receive({}); - }); - }); - - - it('should report invalid JSONata expression', function (done) { - var flow = [{id: "n1", type: "inject", props: [{p:"topic", v:"t1", vt:"str"}, {p:"payload", v:"@", vt:"jsonata"}], wires: [["n2"]], z: "flow"}, - {id: "n2", type: "helper"}]; - helper.load(injectNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - msg.should.have.property("topic", "t1"); - msg.should.not.have.property("payload"); - count++; - if (count == 2) { - done(); - } - } catch (err) { - done(err); - } - }); - n1.on("call:error", function(err) { - count++; - if (count == 2) { - done(); - } - }); - n1.receive({}); - }); - }); - - describe('post', function() { - it('should inject message', function(done) { - helper.load(injectNode, - [{id:"n1", type:"inject", - payloadType:"str", topic: "t4",payload:"hello", - wires:[["n4"]] }, - { id:"n4", type:"helper"}], function() { - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - msg.should.have.property('topic', 't4'); - msg.should.have.property('payload', 'hello'); - helper.clearFlows().then(function() { - done(); - }); - }); - try { - helper.request() - .post('/inject/n1') - .expect(200).end(function(err) { - if (err) { - console.log(err); - return helper.clearFlows() - .then(function () { - done(err); - }); - } - }); - } catch(err) { - done(err); - } - }); - }); - - it('should inject custom properties in posted message', function(done) { - var flow = [{id:"n1", type:"inject", payloadType:"str", topic: "t4",payload:"hello", wires:[["n4"]] }, - { id:"n4", type:"helper"}]; - helper.load(injectNode, flow, function() { - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - msg.should.not.have.property("payload"); //payload removed - msg.should.have.property("topic", "t_override"); //changed value to t_override - msg.should.have.property("str1", "1"); //injected prop - msg.should.have.property("num1", 1); //injected prop - msg.should.have.property("bool1", true); //injected prop - - helper.clearFlows().then(function() { - done(); - }); - }); - try { - helper.request() - .post('/inject/n1') - .send({ __user_inject_props__: [ - {p:"topic", v:"t_override", vt:"str"}, //change value to t_override - {p:"str1", v:"1", vt:"str"}, //new prop - {p:"num1", v:"1", vt:"num"}, //new prop - {p:"bool1", v:"true", vt:"bool"}, //new prop - ]}) - .expect(200).end(function(err) { - if (err) { - console.log(err); - return helper.clearFlows() - .then(function () { - done(err); - }); - } - }); - } catch(err) { - done(err); - } - }); - }); - - it('should fail for invalid node', function(done) { - helper.request().post('/inject/invalid').expect(404).end(done); - }); - }); -}); diff --git a/test/nodes/core/common/21-debug_spec.js b/test/nodes/core/common/21-debug_spec.js deleted file mode 100644 index ae0d9a48e..000000000 --- a/test/nodes/core/common/21-debug_spec.js +++ /dev/null @@ -1,659 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var debugNode = require("nr-test-utils").require("@node-red/nodes/core/common/21-debug.js"); -var helper = require("node-red-node-test-helper"); -var WebSocket = require('ws'); - -describe('debug node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - beforeEach(function (done) { - setTimeout(function() { - done(); - }, 55); - }); - - afterEach(function() { - helper.unload(); - }); - - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"debug", name:"Debug", complete:"false" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'Debug'); - n1.should.have.property('complete', "payload"); - done(); - }); - }); - - it('should publish on input', function(done) { - var flow = [{id:"n1", type:"debug", name:"Debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",name:"Debug",msg:"test",path:"global", - format:"string[4]",property:"payload"} - }]); - }, done); - }); - }); - - it('should publish to console', function(done) { - var flow = [{id:"n1", type:"debug", console:"true"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - var count = 0; - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string[4]",path:"global"} - }]); - count++; - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO, id:'n1',type:'debug',msg:'test', timestamp:tstmp,path:"global"}); - - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish complete message', function(done) { - var flow = [{id:"n1", type:"debug", complete:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg:'{"payload":"test"}',format:"Object",path:"global"} - }]); - }, done); - }); - }); - - it('should publish complete message to console', function(done) { - var flow = [{id:"n1", type:"debug", complete:"true", console:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg:'{"payload":"test"}',format:"Object",path:"global"} - }]); - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO, id:"n1",type:"debug",msg:'\n{ payload: \'test\' }',timestamp:tstmp,path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish other property', function(done) { - var flow = [{id:"n1", type:"debug", complete:"foo" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test", foo:"bar"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string[3]",path:"global"} - }]); - }, done); - }); - }); - - it('should publish multi-level properties', function(done) { - var flow = [{id:"n1", type:"debug", complete:"foo.bar" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test", foo: {bar:"bar"}}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string[3]",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an Error', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: new Error("oops")}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'{"name":"Error","message":"oops"}',property:"payload",format:"error",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a boolean', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: true}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg: 'true',property:"payload",format:"boolean",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a number', function(done) { - var flow = [{id:"n1", type:"debug", console:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: 7}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"7",property:"payload",format:"number",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a NaN', function(done) { - var flow = [{id:"n1", type:"debug", console:"true" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Number.NaN}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"NaN",property:"payload",format:"number",path:"global"} - }]); - }, done); - }); - }); - - it('should publish with no payload', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"undefined",path:"global"} - }]); - }, done); - }); - }); - - it('should publish a null', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:null}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'(undefined)',property:"payload",format:"null",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an object', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {type:'foo'}}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an array', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: [0,1,2,3]}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{id:"n1",msg: '[0,1,2,3]',format:"array[4]", - property:"payload",path:"global"} - }]); - }, done); - }); - }); - - it('should publish an object with circular references', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - var o = { name: 'bar' }; - o.o = o; - n1.emit("input", {payload: o}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:'{"name":"bar","o":"[Circular ~]"}', - property:"payload",format:"Object",path:"global" - } - }]); - }, done); - }); - }); - - it('should publish an object to console', function(done) { - var flow = [{id:"n1", type:"debug", console:"true"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {type:'foo'}}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:'{"type":"foo"}',property:"payload",format:"Object",path:"global"} - }]); - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:'\n{ type: \'foo\' }',timestamp:tstmp,path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish a string after a newline to console if the string contains \\n', function(done) { - var flow = [{id:"n1", type:"debug", console:"true"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test\ntest"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"test\ntest",property:"payload",format:"string[9]",path:"global"} - }]); - }, function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "debug"; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().INFO,id:"n1",type:"debug",msg:"\ntest\ntest",timestamp:tstmp,path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should publish complete message with edit', function(done) { - var flow = [{id:"n1", type:"debug", name:"Debug", complete: "true", - targetType: "jsonata", complete: '"<" & payload & ">"'}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"test"}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",name:"Debug",msg:"", - format:"string[6]",path:"global"} - }]); - }, done); - }); - }); - - it('should truncate a long message', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:Array(1002).join("X")}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg: Array(1001).join("X")+'...', - property:"payload", - format:"string[1001]", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a long string in the object', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {foo: Array(1002).join("X")}}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:'{"foo":"'+Array(1001).join("X")+'..."}', - property:"payload", - format:"Object", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a large array', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Array(1001).fill("X")}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:JSON.stringify({ - __enc__: true, - type: "array", - data: Array(1000).fill("X"), - length: 1001 - }), - property:"payload", - format:"array[1001]", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a large array in the object', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {foo: Array(1001).fill("X")}}); - }, function(msg) { - var a = JSON.parse(msg); - a.should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:JSON.stringify({ - foo:{ - __enc__: true, - type: "array", - data: Array(1000).fill("X"), - length: 1001 - } - }), - property:"payload", - format:"Object", - path:"global" - } - }]); - }, done); - }); - }); - - it('should truncate a large buffer', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Buffer.alloc(501,"\"")}); - }, function(msg) { - var a = JSON.parse(msg); - a[0].should.eql({ - topic:"debug", - data:{ - id:"n1", - msg: Array(1001).join("2"), - property:"payload", - format:"buffer[501]", - path:"global" - } - }); - }, done); - }); - }); - - it('should truncate a large buffer in the object', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: {foo: Buffer.alloc(1001,"X")}}); - }, function(msg) { - var a = JSON.parse(msg); - a[0].should.eql({ - topic:"debug", - data:{ - id:"n1", - msg:JSON.stringify({ - foo:{ - type: "Buffer", - data: Array(1000).fill(88), - __enc__: true, - length: 1001 - } - }), - property:"payload", - format:"Object", - path:"global" - } - }); - }, done); - }); - }); - - it('should convert Buffer to hex', function(done) { - var flow = [{id:"n1", type:"debug" }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload: Buffer.from('HELLO', 'utf8')}); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug", - data:{ - id:"n1", - msg:'48454c4c4f', - property:"payload", - format:"buffer[5]", - path:"global" - } - }]); - }, done); - }); - }); - - it('should publish when active', function(done) { - var flow = [{id:"n1", type:"debug", active: false }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function() { - n1.emit("input", {payload:"message 1"}); - helper.request() - .post('/debug/n1/enable') - .expect(200).end(function(err) { - if (err) { return done(err); } - n1.emit("input", {payload:"message 2"}); - }); - }, function(msg) { - JSON.parse(msg).should.eql([{ - topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string[9]",path:"global"} - }]); - }, done); - }); - }); - - it('should not publish when inactive', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - var n1 = helper.getNode("n1"); - websocket_test(function(close) { - helper.request() - .post('/debug/n1/disable') - .expect(201).end(function(err) { - if (err) { - close(); - return done(err); - } - n1.emit("input", {payload:"message"}); - setTimeout(function() { - close(); - done(); - }, 200); - }); - }, function(msg) { - should.fail(null,null,"unexpected message"); - }, function() {}); - }); - }); - - describe('post', function() { - it('should return 404 on invalid state', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - helper.request() - .post('/debug/n1/foobar') - .expect(404).end(done); - }); - }); - - it('should return 404 on invalid node', function(done) { - helper.request() - .post('/debug/n99/enable') - .expect(404).end(done); - }); - - it('should return 400 for invalid bulk disable', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - helper.request() - .post('/debug/disable') - .send({}) - .set('Content-type', 'application/json') - .expect(400).end(done); - }); - - }) - - it('should return success for bulk disable', function(done) { - var flow = [{id:"n1", type:"debug", active: true }]; - helper.load(debugNode, flow, function() { - helper.request() - .post('/debug/disable') - .send({nodes:['n1']}) - .set('Content-type', 'application/json') - .expect(201).end(done); - }); - - }) - }); - - describe('get', function() { - it('should return the view.html', function(done) { - var flow = [{id:"n1", type:"debug"}]; - helper.load(debugNode, flow, function() { - helper.request() - .get('/debug/view/view.html') - .expect(200) - .end(done); - }); - }); - }); - -}); - -function websocket_test(open_callback, message_callback, done_callback) { - var ws = new WebSocket(helper.url() + "/comms"); - var close_callback = function() { ws.close(); }; - ws.on('open', function() { open_callback(close_callback); }); - ws.on('message', function(msg) { - try { - message_callback(msg, close_callback); - ws.close(); - done_callback(); - } catch(err) { - done_callback(err); - } - }); -} diff --git a/test/nodes/core/common/25-catch_spec.js b/test/nodes/core/common/25-catch_spec.js deleted file mode 100644 index 466d912d7..000000000 --- a/test/nodes/core/common/25-catch_spec.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); -var helper = require("node-red-node-test-helper"); - -describe('catch Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should output a message when called', function(done) { - var flow = [ { id:"n1", type:"catch", name:"catch", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(catchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.should.have.property('name', 'catch'); - n2.on("input", function(msg) { - msg.should.be.a.Error(); - msg.toString().should.equal("Error: big error"); - done(); - }); - var err = new Error("big error"); - n1.emit("input", err); - }); - }); - -}); diff --git a/test/nodes/core/common/25-status_spec.js b/test/nodes/core/common/25-status_spec.js deleted file mode 100644 index 41b0a79c8..000000000 --- a/test/nodes/core/common/25-status_spec.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-status.js"); -var helper = require("node-red-node-test-helper"); - -describe('status Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should output a message when called', function(done) { - var flow = [ { id:"n1", type:"status", name:"status", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(catchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.should.have.property('name', 'status'); - n2.on("input", function(msg) { - msg.text.should.equal("Oh dear"); - msg.should.have.property('source'); - msg.source.should.have.property('id',"12345"); - msg.source.should.have.property('type',"testnode"); - msg.source.should.have.property('name',"fred"); - done(); - }); - var mst = { - text: "Oh dear", - source: { - id: "12345", - type: "testnode", - name: "fred" - } - } - n1.emit("input", mst); - }); - }); - -}); diff --git a/test/nodes/core/common/60-link_spec.js b/test/nodes/core/common/60-link_spec.js deleted file mode 100644 index 5314eed15..000000000 --- a/test/nodes/core/common/60-link_spec.js +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var linkNode = require("nr-test-utils").require("@node-red/nodes/core/common/60-link.js"); -var helper = require("node-red-node-test-helper"); - -describe('link Node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded (link in)', function(done) { - var flow = [{id:"n1", type:"link in", name: "link-in" }]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'link-in'); - done(); - }); - }); - - it('should be loaded (link out)', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out" }]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'link-out'); - done(); - }); - }); - - it('should be linked', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2"]}, - {id:"n2", type:"link in", name: "link-in", wires:[["n3"]]}, - {id:"n3", type:"helper"}]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - var n3 = helper.getNode("n3"); - n3.on("input", function(msg) { - try { - msg.should.have.property('payload', 'hello'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - }); - }); - - it('should be linked to multiple nodes', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out", links:["n2", "n3"]}, - {id:"n2", type:"link in", name: "link-in0", wires:[["n4"]]}, - {id:"n3", type:"link in", name: "link-in1", wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - var n4 = helper.getNode("n4"); - var count = 0; - n4.on("input", function (msg) { - try { - msg.should.have.property('payload', 'hello'); - count++; - if(count == 2) { - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - }); - }); - - it('should be linked from multiple nodes', function(done) { - var flow = [{id:"n1", type:"link out", name: "link-out0", links:["n3"]}, - {id:"n2", type:"link out", name: "link-out1", links:["n3"]}, - {id:"n3", type:"link in", name: "link-in", wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n4 = helper.getNode("n4"); - var count = 0; - n4.on("input", function(msg) { - try { - msg.should.have.property('payload', 'hello'); - count++; - if(count == 2) { - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - n2.receive({payload:"hello"}); - }); - }); - - describe("link-call node", function() { - it('should call link-in node and get response', function(done) { - var flow = [{id:"link-in-1", type:"link in", wires: [[ "func"]]}, - {id:"func", type:"helper", wires: [["link-out-1"]]}, - {id:"link-out-1", type:"link out", mode: "return"}, - {id:"link-call", type:"link call", links:["link-in-1"], wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var func = helper.getNode("func"); - func.on("input", function(msg, send, done) { - msg.payload = "123"; - send(msg); - done(); - }) - var n1 = helper.getNode("link-call"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property('payload', '123'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"hello"}); - }); - }) - }); - - it('should allow nested link-call flows', function(done) { - var flow = [/** Multiply by 2 link flow **/ - {id:"li1", type:"link in", wires: [[ "m2"]]}, - {id:"m2", type:"helper", wires: [["lo1"]]}, - {id:"lo1", type:"link out", mode: "return"}, - /** Multiply by 3 link flow **/ - {id:"li2", type:"link in", wires: [[ "m3"]]}, - {id:"m3", type:"helper", wires: [["lo2"]]}, - {id:"lo2", type:"link out", mode: "return"}, - /** Multiply by 6 link flow **/ - {id:"li3", type:"link in", wires: [[ "link-call-1"]]}, - {id:"link-call-1", type:"link call", links:["m2"], wires:[["link-call-2"]]}, - {id:"link-call-2", type:"link call", links:["m3"], wires:[["lo3"]]}, - {id:"lo3", type:"link out", mode: "return"}, - /** Test Flow Entry **/ - {id:"link-call", type:"link call", links:["li3"], wires:[["n4"]]}, - {id:"n4", type:"helper"} ]; - helper.load(linkNode, flow, function() { - var m2 = helper.getNode("m2"); - m2.on("input", function(msg, send, done) { msg.payload *= 2 ; send(msg); done(); }) - var m3 = helper.getNode("m3"); - m3.on("input", function(msg, send, done) { msg.payload *= 3 ; send(msg); done(); }) - - var n1 = helper.getNode("link-call"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property('payload', 24); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:4}); - }); - }) -}); diff --git a/test/nodes/core/common/90-comment_spec.js b/test/nodes/core/common/90-comment_spec.js deleted file mode 100644 index a6f6a8284..000000000 --- a/test/nodes/core/common/90-comment_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var commentNode = require("nr-test-utils").require("@node-red/nodes/core/common/90-comment.js"); -var helper = require("node-red-node-test-helper"); - -describe('comment Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"comment", name: "comment" }]; - helper.load(commentNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'comment'); - done(); - }); - }); - -}); diff --git a/test/nodes/core/common/98-unknown_spec.js b/test/nodes/core/common/98-unknown_spec.js deleted file mode 100644 index 1b16a5c3b..000000000 --- a/test/nodes/core/common/98-unknown_spec.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var unknown = require("nr-test-utils").require("@node-red/nodes/core/common/98-unknown.js"); -var helper = require("node-red-node-test-helper"); - -describe('unknown Node', function() { - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"unknown", name: "unknown" }]; - helper.load(unknown, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'unknown'); - done(); - }); - }); - -}); diff --git a/test/nodes/core/function/10-function_spec.js b/test/nodes/core/function/10-function_spec.js deleted file mode 100644 index 4f7f0b806..000000000 --- a/test/nodes/core/function/10-function_spec.js +++ /dev/null @@ -1,1721 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -var should = require("should"); -var functionNode = require("nr-test-utils").require("@node-red/nodes/core/function/10-function.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); -describe('function node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory1: { - module: "memory" - }, - memory2: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function() { - helper.unload().then(function () { - return Context.clean({allNodes:{}}); - }).then(function () { - return Context.close(); - }); - }); - - it('should send returned message using send()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should do something with the catch node', function(done) { - var flow = [{"id":"funcNode","type":"function","wires":[["goodNode"]],"func":"node.error('This is an error', msg);"},{"id":"goodNode","type":"helper"},{"id":"badNode","type":"helper"},{"id":"catchNode","type":"catch","scope":null,"uncaught":false,"wires":[["badNode"]]}]; - var catchNodeModule = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js") - helper.load([catchNodeModule, functionNode], flow, function() { - var funcNode = helper.getNode("funcNode"); - var catchNode = helper.getNode("catchNode"); - var goodNode = helper.getNode("goodNode"); - var badNode = helper.getNode("badNode"); - - badNode.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - msg.should.have.property('error'); - msg.error.should.have.property('message',"This is an error"); - msg.error.should.have.property('source'); - msg.error.source.should.have.property('id', "funcNode"); - done(); - }); - funcNode.receive({payload:"foo",topic: "bar"}); - }); - }); - - - - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"function", name: "function" }]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'function'); - done(); - }); - }); - - it('should send returned message', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should send returned message using send()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing node.id and node.name and node.outputCount', function(done) { - var flow = [{id:"n1",name:"test-function", outputs: 2, type:"function",wires:[["n2"]],func: "return [{ topic: node.name, payload:node.id, outputCount: node.outputCount }];"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - // Use this form of assert as `msg` is created inside - // the sandbox and doesn't get all the should.js monkey patching - should.equal(msg.payload, n1.id); - should.equal(msg.topic, n1.name); - should.equal(msg.outputCount, n1.outputs); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - function testSendCloning(args,done) { - var flow = [{id:"n1",type:"function",wires:[["n2"],["n2"]],func:"node.send("+args+"); msg.payload = 'changed';"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - } catch(err) { - done(err); - } - }); - var origMessage = {payload:"foo",topic: "bar"}; - n1.receive(origMessage); - }); - } - it('should clone single message sent using send()', function(done) { - testSendCloning("msg",done); - }); - it('should not clone single message sent using send(,false)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.send(msg,false); msg.payload = 'changed';"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'changed'); - done(); - }); - var origMessage = {payload:"foo",topic: "bar"}; - n1.receive(origMessage); - }); - }); - it('should clone first message sent using send() - array 1', function(done) { - testSendCloning("[msg]",done); - }); - it('should clone first message sent using send() - array 2', function(done) { - testSendCloning("[[msg],[null]]",done); - }); - it('should clone first message sent using send() - array 3', function(done) { - testSendCloning("[null,msg]",done); - }); - it('should clone first message sent using send() - array 3', function(done) { - testSendCloning("[null,[msg]]",done); - }); - - it('should pass through _topic', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - msg.should.have.property('_topic', 'baz'); - done(); - }); - n1.receive({payload:"foo",topic: "bar", _topic: "baz"}); - }); - }); - - it('should send to multiple outputs', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"],["n3"]], - func:"return [{payload: '1'},{payload: '2'}];"}, - {id:"n2", type:"helper"}, {id:"n3", type:"helper"} ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var count = 0; - n2.on("input", function(msg) { - should(msg).have.property('payload', '1'); - count++; - if (count == 2) { - done(); - } - }); - n3.on("input", function(msg) { - should(msg).have.property('payload', '2'); - count++; - if (count == 2) { - done(); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should send to multiple messages', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]], - func:"return [[{payload: 1},{payload: 2}]];"}, - {id:"n2", type:"helper"} ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - count++; - try { - should(msg).have.property('payload', count); - should(msg).have.property('_msgid', 1234); - if (count == 2) { - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", topic: "bar",_msgid:1234}); - }); - }); - - it('should allow input to be discarded by returning null', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return null"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function() { - done(); - }, 20); - n2.on("input", function(msg) { - should.fail(null,null,"unexpected message"); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle null amongst valid messages', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"return [[msg,null,msg],null]"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n2MsgCount = 0; - var n3MsgCount = 0; - n2.on("input", function(msg) { - n2MsgCount++; - }); - n3.on("input", function(msg) { - n3MsgCount++; - }); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - n2MsgCount.should.equal(2); - n3MsgCount.should.equal(0); - done(); - },20); - }); - }); - - it('should get keys in global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.keys();return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - function testNonObjectMessage(functionText,done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:functionText}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n2MsgCount = 0; - n2.on("input", function(msg) { - n2MsgCount++; - }); - n1.receive({}); - setTimeout(function() { - try { - n2MsgCount.should.equal(0); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'function.error.non-message-returned'); - done(); - } catch(err) { - done(err); - } - },20); - }); - } - it('should drop and log non-object message types - string', function(done) { - testNonObjectMessage('return "foo"', done) - }); - it('should drop and log non-object message types - buffer', function(done) { - testNonObjectMessage('return Buffer.from("hello")', done) - }); - it('should drop and log non-object message types - array', function(done) { - testNonObjectMessage('return [[[1,2,3]]]', done) - }); - it('should drop and log non-object message types - boolean', function(done) { - testNonObjectMessage('return true', done) - }); - it('should drop and log non-object message types - number', function(done) { - testNonObjectMessage('return 123', done) - }); - - it('should handle and log script error', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"var a = 1;\nretunr"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'ReferenceError: retunr is not defined (line 2, col 1)'); - done(); - } catch(err) { - done(err); - } - },50); - }); - }); - - it('should handle node.on()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"node.on('close',function(){ node.log('closed')});"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - n1.close().then(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().INFO); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'closed'); - done(); - } catch(err) { - done(err); - } - }); - },100); - }); - }); - - it('should set node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count").should.equal("0"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should set persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1');context.set('count','1','memory2');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count", "memory2", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (single call, w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set(['count1','count2'],['0','1'],'memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count1", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count2", "memory1", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - - it('should set persistable node context (w callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1', function (err) { node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (w callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0','memory1', function (err) { context.set('count', '1', 'memory2', function (err) { node.send(msg); }); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count", "memory1", function (err, val2) { - val2.should.equal("0"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable node context (single call, w callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set(['count1','count2'],['0','1'],'memory1', function(err) { node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count1", "memory1", function (err, val1) { - val1.should.equal("0"); - n1.context().get("count2", "memory1", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - - it('should set default persistable node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.set('count','0');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n1.context().get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.get('count');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - function checkCallbackError(name, done) { - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', name); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'Error: Callback must be a function'); - done(); - } - catch (e) { - done(e); - } - },50); - } - - it('should get persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.get('count','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable node context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.get('count','memory1',function (err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys();return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get keys in persistable node context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys('memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in persistable node context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.keys('memory1', function(err, keys) { msg.payload=keys; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in default persistable node context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.keys();return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().set("count","0","memory1"); - n1.context().set("number","1","memory2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count").should.equal("0"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should set persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1');flow.set('count','1','memory2');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n2.context().flow.get("count", "memory2", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1', function (err) { node.send(msg); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set two persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.set('count','0','memory1', function (err) { flow.set('count','1','memory2', function (err) { node.send(msg); }); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().flow.get("count", "memory1", function (err, val1) { - val1.should.equal("0"); - n2.context().flow.get("count", "memory2", function (err, val2) { - val2.should.equal("1"); - done(); - }); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.get('count');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.get('count','memory1');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.get('count','memory1', function(err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.flow.get('count');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get keys in flow context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.keys();return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get keys in persistable flow context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=flow.keys('memory1');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get keys in persistable flow context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"flow.keys('memory1', function (err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', ['count']); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().global.get("count").should.equal("0"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should set persistable global context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().global.get("count", "memory1", function(err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should set persistable global context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.set('count','0','memory1', function (err) { node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - n2.context().global.get("count", "memory1", function(err, val) { - val.should.equal("0"); - done(); - }); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('count');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get persistable global context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('count', 'memory1');return msg;"}, - {id:"n2", type:"helper"}]; - initContext(function () { - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", 'memory1'); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable global context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"global.get('count', 'memory1', function (err, val) { msg.payload=val; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - initContext(function () { - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", 'memory1'); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get global context', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.global.get('count');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should get persistable global context (w/o callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=context.global.get('count','memory1');return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", "memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should get persistable global context (w/ callback)', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"context.global.get('count','memory1', function (err, val) { msg.payload = val; node.send(msg); });"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("count","0", "memory1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '0'); - done(); - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should handle error on get persistable context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.get('count','memory1','callback');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n1.receive({payload:"foo",topic: "bar"}); - checkCallbackError('n1', done); - }); - }); - }); - - it('should handle error on set persistable context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.set('count','0','memory1','callback');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:"foo",topic: "bar"}); - checkCallbackError('n1', done); - }); - }); - }); - - it('should handle error on get keys in persistable context', function(done) { - var flow = [{id:"n1",type:"function",z:"flowA",wires:[["n2"]],func:"msg.payload=context.keys('memory1','callback');return msg;"}, - {id:"n2", type:"helper",z:"flowA"}]; - helper.load(functionNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("count","0","memory1"); - n1.receive({payload:"foo",topic: "bar"}); - checkCallbackError('n1', done); - }); - }); - }); - - it('should handle setTimeout()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"setTimeout(function(){node.send(msg);},700);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var endTime = process.hrtime(startTime); - var nanoTime = endTime[0] * 1000000000 + endTime[1]; - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - if (600000000 < nanoTime && nanoTime < 800000000) { - done(); - } else { - try { - should.fail(null, null, "Delayed time was not between 900 and 1100 ms"); - } catch (err) { - done(err); - } - } - }); - var startTime = process.hrtime(); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle setInterval()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"setInterval(function(){node.send(msg);},100);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - count++; - if (count > 2) { - done(); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle clearInterval()', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"var id=setInterval(null,100);setTimeout(function(){clearInterval(id);node.send(msg);},500);"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing node.id', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = node.id; return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', n1.id); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing node.name', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = node.name; return msg;", "name":"name of node"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', n1.name); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should use the same Date object from outside the sandbox', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload=global.get('typeTest')(new Date());return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("typeTest",function(d) { return d instanceof Date }); - n2.on("input", function(msg) { - msg.should.have.property('payload', true); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should allow accessing env vars', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = env.get('_TEST_FOO_'); return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - delete process.env._TEST_FOO_; - - n2.on("input", function(msg) { - try { - if (count === 0) { - msg.should.have.property('payload', undefined); - process.env._TEST_FOO_ = "hello"; - count++; - n1.receive({payload:"foo",topic: "bar"}); - } else { - msg.should.have.property('payload', "hello"); - delete process.env._TEST_FOO_; - done(); - } - } catch(err) { - delete process.env._TEST_FOO_; - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should execute initialization', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = global.get('X'); return msg;",initialize:"global.set('X','bar');"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "bar"); - done(); - }); - n1.receive({payload: "foo"}); - }); - }); - - it('should wait completion of initialization', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = global.get('X'); return msg;",initialize:"global.set('X', '-'); return new Promise((resolve, reject) => setTimeout(() => { global.set('X','bar'); resolve(); }, 500));"}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "bar"); - done(); - }); - n1.receive({payload: "foo"}); - }); - }); - - - - describe("finalize function", function() { - - it('should execute', function(done) { - var flow = [{id:"n1",type:"function",wires:[],func:"return msg;",finalize:"global.set('X','bar');"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var ctx = n1.context().global; - helper.unload().then(function () { - ctx.get('X').should.equal("bar"); - done(); - }); - }); - }); - - it('should allow accessing node.id and node.name and node.outputCount', function(done) { - var flow = [{id:"n1",name:"test-function", outputs: 2, type:"function",wires:[["n2"]],finalize:"global.set('finalize-data', { topic: node.name, payload:node.id, outputCount: node.outputCount});", func: "return msg;"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var ctx = n1.context().global; - helper.unload().then(function () { - const finalizeData = ctx.get('finalize-data'); - should.equal(finalizeData.payload, n1.id); - should.equal(finalizeData.topic, n1.name); - should.equal(finalizeData.outputCount, n1.outputs); - done(); - }); - }); - }); - - }) - - describe('externalModules', function() { - afterEach(function() { - delete RED.settings.functionExternalModules; - }) - it('should fail if using OS module with functionExternalModules set to false', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"os", module:"os"}]}, - {id:"n2", type:"helper"} - ]; - RED.settings.functionExternalModules = false; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - should.not.exist(n1); - done(); - }).catch(err => done(err)); - }) - - it('should fail if using OS module without it listed in libs', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;"}, - {id:"n2", type:"helper"} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var messageReceived = false; - n2.on("input", function(msg) { - messageReceived = true; - }); - n1.receive({payload:"foo",topic: "bar"}); - setTimeout(function() { - try { - messageReceived.should.be.false(); - done(); - } catch(err) { - done(err); - } - },20); - }).catch(err => done(err)); - }) - it('should require the OS module', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"os", module:"os"}]}, - {id:"n2", type:"helper"} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', require('os').type()); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }).catch(err => done(err)); - }) - it('should fail if module variable name clashes with sandbox builtin', function(done) { - var flow = [ - {id:"n1",type:"function",wires:[["n2"]],func:"msg.payload = os.type(); return msg;", "libs": [{var:"flow", module:"os"}]}, - {id:"n2", type:"helper"} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - should.not.exist(n1); - done(); - }).catch(err => done(err)); - }) - }) - - - describe('Logger', function () { - - function testLog(initCode,funcCode,expectedLevel, done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: funcCode, initialize: initCode}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log()[expectedLevel]); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'test'); - done(); - } catch (err) { - done(err); - } - },10); - }); - } - - it('should log an Info Message', function (done) { - testLog("","node.log('test');","INFO",done); - }); - it('should log a Debug Message', function (done) { - testLog("","node.debug('test');","DEBUG",done); - }); - it('should log a Trace Message', function (done) { - testLog("","node.trace('test');","TRACE",done); - }); - it('should log a Warning Message', function (done) { - testLog("","node.warn('test');","WARN",done); - }); - it('should log an Error Message', function (done) { - testLog("","node.error('test');","ERROR",done); - }); - - it('should log an Info Message - initialise', function (done) { - testLog("node.log('test');","","INFO",done); - }); - it('should log a Debug Message - initialise', function (done) { - testLog("node.debug('test');","","DEBUG",done); - }); - it('should log a Trace Message - initialise', function (done) { - testLog("node.trace('test');","","TRACE",done); - }); - it('should log a Warning Message - initialise', function (done) { - testLog("node.warn('test');","","WARN",done); - }); - it('should log an Error Message - initialise', function (done) { - testLog("node.error('test');","","ERROR",done); - }); - - it('should catch thrown string', function (done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "throw \"small mistake\";"}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', 'small mistake'); - done(); - } catch (err) { - done(err); - } - },50); - }); - }); - it('should catch thrown number', function (done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "throw 99;"}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', '99'); - done(); - } catch (err) { - done(err); - } - },50); - }); - }); - it('should catch thrown object (bad practice)', function (done) { - var flow = [{id: "n1", type: "function", wires: [["n2"]], func: "throw {a:1};"}]; - helper.load(functionNode, flow, function () { - var n1 = helper.getNode("n1"); - n1.receive({payload: "foo", topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "function"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'n1'); - msg.should.have.property('type', 'function'); - msg.should.have.property('msg', '{"a":1}'); - done(); - } catch (err) { - done(err); - } - },50); - }); - }); - }); - - describe("init function", function() { - - it('should delay handling messages until init completes', function(done) { - var flow = [{id:"n1",type:"function",wires:[["n2"]],initialize: ` - return new Promise((resolve,reject) => { - setTimeout(resolve,200) - })`, - func:"return msg;" - }, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var receivedMsgs = []; - n2.on("input", function(msg) { - msg.delta = Date.now() - msg.payload; - receivedMsgs.push(msg) - if (receivedMsgs.length === 5) { - var errors = receivedMsgs.filter(msg => msg.delta < 200) - if (errors.length > 0) { - done(new Error(`Message received before init completed - was ${msg.delta} expected >300`)) - } else { - done(); - } - } - }); - for (var i=0;i<5;i++) { - n1.receive({payload: Date.now(),topic: "msg"+i}); - } - }); - }); - - it('should allow accessing node.id and node.name and node.outputCount and sending message', function(done) { - var flow = [{id:"n1",name:"test-function", outputs: 1, type:"function",wires:[["n2"]],initialize:"setTimeout(function() { node.send({ topic: node.name, payload:node.id, outputCount: node.outputCount})},10)", func: ""}, - {id:"n2", type:"helper"}]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - // Use this form of assert as `msg` is created inside - // the sandbox and doesn't get all the should.js monkey patching - should.equal(msg.payload, n1.id); - should.equal(msg.topic, n1.name); - should.equal(msg.outputCount, n1.outputs); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - }); -}); diff --git a/test/nodes/core/function/10-switch_spec.js b/test/nodes/core/function/10-switch_spec.js deleted file mode 100644 index 180ec9d36..000000000 --- a/test/nodes/core/function/10-switch_spec.js +++ /dev/null @@ -1,1153 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var switchNode = require("nr-test-utils").require("@node-red/nodes/core/function/10-switch.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context/"); - -describe('switch Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - RED.settings.nodeMessageBufferMaxLength = 0; - helper.stopServer(done); - }); - }); - - it('should be loaded with some defaults', function(done) { - var flow = [{"id":"switchNode1","type":"switch","name":"switchNode"}]; - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - switchNode1.should.have.property('name', 'switchNode'); - switchNode1.should.have.property('checkall', "true"); - switchNode1.should.have.property('rules', []); - done(); - }); - }); - - /** - * Test a switch node where one argument is consumed by the rule (such as greater than). - * @param rule - the switch rule (see 10-switc.js) string we're using - * @param ruleWith - whatever the rule should be executed with (say greater than 5) - * @param aCheckall - whether the switch flow should have the checkall flag set to true/false - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function genericSwitchTest(rule, ruleWith, aCheckall, shouldReceive, sendPayload, done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule,"v":ruleWith}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, shouldReceive, sendPayload, done); - } - - /** - * Test a switch node where NO arguments are consumed by the rule (such as TRUE/FALSE) - * @param rule - the switch rule (see 10-switc.js) string we're using - * @param aCheckall - whether the switch flow should have the checkall flag set to true/false - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function singularSwitchTest(rule, aCheckall, shouldReceive, sendPayload, done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, shouldReceive, sendPayload, done); - } - - /** - * Test a switch node where two arguments are consumed by the rule (such as between). - * @param rule - the switch rule (see 10-switc.js) string we're using - * @param ruleWith - whatever the rule should be executed with (say between 5...) - * @param ruleWith2 - whatever the rule should be executed with (say ...and 5) - * @param aCheckall - whether the switch flow should have the checkall flag set to true/false - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function twoFieldSwitchTest(rule, ruleWith, ruleWith2, aCheckall, shouldReceive, sendPayload, done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":rule,"v":ruleWith,"v2":ruleWith2}],checkall:aCheckall,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, shouldReceive, sendPayload, done); - } - - /** - * Execute a switch test. Can specify whether the should node is expected to send a payload onwards to the helper node. - * The flow and the payload can be customised - * @param flow - the custom flow to be tested => must contain a switch node (switchNode1) wiring a helper node (helperNode1) - * @param shouldReceive - whether the helper node should receive a payload - * @param sendPayload - the payload message we're sending - * @param done - callback when done - */ - function customFlowSwitchTest(flow, shouldReceive, sendPayload, done) { - customFlowMessageSwitchTest(flow,shouldReceive,{payload: sendPayload}, done); - } - - function customFlowMessageSwitchTest(flow, shouldReceive, message, done) { - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - if (shouldReceive === true) { - should.equal(msg,message); - done(); - } else { - should.fail(null, null, "We should never get an input!"); - } - } catch(err) { - done(err); - } - }); - switchNode1.receive(message); - if (shouldReceive === false) { - setTimeout(function() { - done(); - }, 200); - } - }); - } - - function customFlowSequenceSwitchTest(flow, seq_in, seq_out, repair, modifier, done) { - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var sid; - var count = 0; - if (modifier !== undefined) { - modifier(switchNode1); - } - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("payload", seq_out[count]); - msg.should.have.property("parts"); - var parts = msg.parts; - parts.should.have.property("id"); - var id = parts.id; - if (sid === undefined) { - sid = id; - } - else { - id.should.equal(sid); - } - if (repair) { - parts.should.have.property("index", count); - parts.should.have.property("count", seq_out.length); - } - else { - parts.should.have.property("index", msg.xindex); - parts.should.have.property("count", seq_in.length); - } - count++; - if (count === seq_out.length) { - done(); - } - } catch (e) { - done(e); - } - }); - var len = seq_in.length; - for (var i = 0; i < len; i++) { - var parts = {index:i, count:len, id:222}; - var msg = {payload:seq_in[i], xindex:i, parts:parts}; - switchNode1.receive(msg); - } - }); - } - - it('should check if payload equals given value', function(done) { - genericSwitchTest("eq", "Hello", true, true, "Hello", done); - }); - - it('should return nothing when the payload doesn\'t equal to desired string', function(done) { - genericSwitchTest("eq", "Hello", true, false, "Hello!", done); - }); - - it('should check if payload NOT equals given value', function(done) { - genericSwitchTest("neq", "Hello", true, true, "HEllO", done); - }); - - it('should return nothing when the payload does equal to desired string', function(done) { - genericSwitchTest("neq", "Hello", true, false, "Hello", done); - }); - - it('should check if payload equals given numeric value', function(done) { - genericSwitchTest("eq", 3, true, true, 3, done); - }); - - it('should return nothing when the payload doesn\'t equal to desired numeric value', function(done) { - genericSwitchTest("eq", 2, true, false, 4, done); - }); - - it('should check if payload NOT equals given numeric value', function(done) { - genericSwitchTest("neq", 55667744, true, true, -1234, done); - }); - - it('should return nothing when the payload does equal to desired numeric value', function(done) { - genericSwitchTest("neq", 10, true, false, 10, done); - }); - - it('should check if payload is less than given value', function(done) { - genericSwitchTest("lt", 3, true, true, 2, done); - }); - - it('should return nothing when the payload is not less than desired string', function(done) { - genericSwitchTest("lt", 3, true, false, 4, done); - }); - - it('should check if payload less than equals given value', function(done) { - genericSwitchTest("lte", 3, true, true, 3, done); - }); - - it('should check if payload is greater than given value', function(done) { - genericSwitchTest("gt", 3, true, true, 6, done); - }); - - it('should return nothing when the payload is not greater than desired string', function(done) { - genericSwitchTest("gt", 3, true, false, -1, done); - }); - - it('should check if payload is greater than/equals given value', function(done) { - genericSwitchTest("gte", 3, true, true, 3, done); - }); - - it('should return nothing when the payload is not greater than desired string', function(done) { - genericSwitchTest("gt", 3, true, false, -1, done); - }); - - it('should check if payload is greater than/equals given value', function(done) { - genericSwitchTest("gte", 3, true, true, 3, done); - }); - - it('should match if a payload has a required property', function(done) { - genericSwitchTest("hask", "a", true, true, {a:1}, done); - }); - it('should not match if a payload does not have a required property', function(done) { - genericSwitchTest("hask", "a", true, false, {b:1}, done); - }); - it('should not match if the key is not a string', function(done) { - genericSwitchTest("hask", 1, true, false, {a:1}, done); - }); - it('should not match if the parent object does not exist - null', function(done) { - genericSwitchTest("hask", "a", true, false, null, done); - }); - it('should not match if the parent object does not exist - undefined', function(done) { - genericSwitchTest("hask", "a", true, false, undefined, done); - }); - it('should check if payload is between given values', function(done) { - twoFieldSwitchTest("btwn", "3", "5", true, true, 4, done); - }); - - it('should check if payload is between given values in "wrong" order', function(done) { - twoFieldSwitchTest("btwn", "5", "3", true, true, 4, done); - }); - - it('should check if payload is between given string values', function(done) { - twoFieldSwitchTest("btwn", "c", "e", true, true, "d", done); - }); - - it('should check if payload is not between given values', function(done) { - twoFieldSwitchTest("btwn", 3, 5, true, false, 12, done); - }); - - it('should check if payload contains given value', function(done) { - genericSwitchTest("cont", "Hello", true, true, "Hello World!", done); - }); - - it('should return nothing when the payload doesn\'t contain desired string', function(done) { - genericSwitchTest("cont", "Hello", true, false, "This is not a greeting!", done); - }); - - it('should match regex', function(done) { - genericSwitchTest("regex", "[abc]+", true, true, "abbabac", done); - }); - - it('should check if payload if of type string ', function(done) { - genericSwitchTest("istype", "string", true, true, "Hello", done); - }); - it('should check if payload if of type number ', function(done) { - genericSwitchTest("istype", "number", true, true, 999, done); - }); - it('should check if payload if of type number 0', function(done) { - genericSwitchTest("istype", "number", true, true, 0, done); - }); - it('should check if payload if of type boolean true', function(done) { - genericSwitchTest("istype", "boolean", true, true, true, done); - }); - it('should check if payload if of type boolean false', function(done) { - genericSwitchTest("istype", "boolean", true, true, true, done); - }); - it('should check if payload if of type array ', function(done) { - genericSwitchTest("istype", "array", true, true, [1,2,3,"a","b"], done); - }); - it('should check if payload if of type buffer ', function(done) { - genericSwitchTest("istype", "buffer", true, true, Buffer.from("Hello"), done); - }); - it('should check if payload if of type object ', function(done) { - genericSwitchTest("istype", "object", true, true, {a:1,b:"b",c:true}, done); - }); - it('should check if payload if of type JSON string ', function(done) { - genericSwitchTest("istype", "json", true, true, JSON.stringify({a:1,b:"b",c:true}), done); - }); - it('should check if payload if of type JSON string (and fail if not) ', function(done) { - genericSwitchTest("istype", "json", true, false, "Hello", done); - }); - it('should check if payload if of type null', function(done) { - genericSwitchTest("istype", "null", true, true, null, done); - }); - it('should check if payload if of type undefined', function(done) { - genericSwitchTest("istype", "undefined", true, true, undefined, done); - }); - - it('should handle flow context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "foo", "propertyType": "flow", - "rules": [{"t": "eq", "v": "bar", "vt": "flow"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]], "z": "flow"}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("value"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().flow.set("foo", "flowValue"); - switchNode1.context().flow.set("bar", "flowValue"); - switchNode1.receive({payload: "value"}); - }); - }); - - it('should handle persistable flow context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "#:(memory1)::foo", "propertyType": "flow", - "rules": [{"t": "eq", "v": "#:(memory1)::bar", "vt": "flow"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]], "z": "flow"}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - initContext(function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("value"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().flow.set("foo", "flowValue", "memory1", function (err) { - switchNode1.context().flow.set("bar", "flowValue", "memory1", function (err) { - switchNode1.receive({payload: "value"}); - }); - }); - }); - }); - }); - - it('should handle global context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "foo", "propertyType": "global", - "rules": [{"t": "eq", "v": "bar", "vt": "global"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]]}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("value"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().global.set("foo", "globalValue"); - switchNode1.context().global.set("bar", "globalValue"); - switchNode1.receive({payload: "value"}); - }); - }); - - it('should handle persistable global context', function (done) { - var flow = [{"id": "switchNode1", "type": "switch", "property": "#:(memory1)::foo", "propertyType": "global", - "rules": [{"t": "eq", "v": "#:(memory1)::bar", "vt": "global"}], - "checkall": "true", "outputs": "1", "wires": [["helperNode1"]]}, - {"id": "helperNode1", "type": "helper", "wires": []}]; - helper.load(switchNode, flow, function () { - initContext(function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function (msg) { - try { - msg.payload.should.equal("foo"); - done(); - } catch (err) { - done(err); - } - }); - switchNode1.context().global.set("foo", "globalValue", "memory1", function (err) { - switchNode1.context().global.set("bar", "globalValue", "memory1", function (err) { - switchNode1.receive({payload: "foo"}); - }); - }); - }); - }); - }); - - it('should use a nested message property to compare value - matches', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"bar"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, true, {topic:"foo",payload:{"foo":"bar"}}, done); - }) - it('should use a nested message property to compare value - no match', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"bar"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, false, {topic:"foo",payload:{"foo":"none"}}, done); - - }) - - it('should use a nested message property to compare nested message property - matches', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"payload[msg.topic2]",vt:"msg"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, true, {topic:"foo",topic2:"foo2",payload:{"foo":"bar","foo2":"bar"}}, done); - }) - it('should use a nested message property to compare nested message property - no match', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload[msg.topic]",rules:[{"t":"eq","v":"payload[msg.topic2]",vt:"msg"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowMessageSwitchTest(flow, false, {topic:"foo",topic2:"foo2",payload:{"foo":"bar","foo2":"none"}}, done); - }) - - it('should match regex with ignore-case flag set true', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree","case":true}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, true, "oneTWOthree", done); - }); - it('should not match regex with ignore-case flag unset', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, false, "oneTWOthree", done); - }); - it('should not match regex with ignore-case flag set false', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree",case:false}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, false, "oneTWOthree", done); - }); - - it('should return nothing when the payload doesn\'t match regex', function(done) { - genericSwitchTest("regex", "\\d+", true, false, "This is not a digit", done); - }); - - it('should return nothing when the payload doesn\'t contain desired string', function(done) { - genericSwitchTest("cont", "Hello", true, false, "This is not a greeting!", done); - }); - - it('should check if input is true', function(done) { - singularSwitchTest(true, true, true, true, done); - }); - - it('sends nothing when input is false and checking for true', function(done) { - singularSwitchTest(true, true, false, false, done); - }); - - it('should check if input is indeed false', function(done) { - singularSwitchTest(false, true, true, false, done); - }); - - it('sends nothing when input is false and checking for true', function(done) { - singularSwitchTest(false, true, false, true, done); - }); - - it('should check if payload is empty (string)', function(done) { - singularSwitchTest("empty", true, true, "", done); - }); - it('should check if payload is empty (array)', function(done) { - singularSwitchTest("empty", true, true, [], done); - }); - it('should check if payload is empty (buffer)', function(done) { - singularSwitchTest("empty", true, true, Buffer.alloc(0), done); - }); - it('should check if payload is empty (object)', function(done) { - singularSwitchTest("empty", true, true, {}, done); - }); - it('should check if payload is empty (non-empty string)', function(done) { - singularSwitchTest("empty", true, false, "1", done); - }); - it('should check if payload is empty (non-empty array)', function(done) { - singularSwitchTest("empty", true, false, [1], done); - }); - it('should check if payload is empty (non-empty buffer)', function(done) { - singularSwitchTest("empty", true, false, Buffer.alloc(1), done); - }); - it('should check if payload is empty (non-empty object)', function(done) { - singularSwitchTest("empty", true, false, {a:1}, done); - }); - it('should check if payload is empty (null)', function(done) { - singularSwitchTest("empty", true, false, null, done); - }); - it('should check if payload is empty (undefined)', function(done) { - singularSwitchTest("empty", true, false, undefined, done); - }); - it('should check if payload is empty (0)', function(done) { - singularSwitchTest("empty", true, false, 0, done); - }); - - it('should check if payload is not empty (string)', function(done) { - singularSwitchTest("nempty", true, !true, "", done); - }); - it('should check if payload is not empty (array)', function(done) { - singularSwitchTest("nempty", true, !true, [], done); - }); - it('should check if payload is not empty (buffer)', function(done) { - singularSwitchTest("nempty", true, !true, Buffer.alloc(0), done); - }); - it('should check if payload is not empty (object)', function(done) { - singularSwitchTest("nempty", true, !true, {}, done); - }); - it('should check if payload is not empty (non-empty string)', function(done) { - singularSwitchTest("nempty", true, !false, "1", done); - }); - it('should check if payload is not empty (non-empty array)', function(done) { - singularSwitchTest("nempty", true, !false, [1], done); - }); - it('should check if payload is not empty (non-empty buffer)', function(done) { - singularSwitchTest("nempty", true, !false, Buffer.alloc(1), done); - }); - it('should check if payload is not empty (non-empty object)', function(done) { - singularSwitchTest("nempty", true, !false, {a:1}, done); - }); - it('should check if payload is not empty (null)', function(done) { - singularSwitchTest("nempty", true, false, null, done); - }); - it('should check if payload is not empty (undefined)', function(done) { - singularSwitchTest("nempty", true, false, undefined, done); - }); - it('should check if payload is not empty (0)', function(done) { - singularSwitchTest("nempty", true, false, 0, done); - }); - - it('should check input against a previous value', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{ "t": "gt", "v": "", "vt": "prev" }],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var c = 0; - helperNode1.on("input", function(msg) { - if (msg.payload) { - try { - if (c === 0) { - msg.payload.should.equal(1); - } - if (c === 1) { - msg.payload.should.equal(2); - done(); - } - c += 1; - } catch (err) { - done(err); - } - } else { - done(); - } - }); - switchNode1.receive({payload:1}); - switchNode1.receive({payload:0}); - switchNode1.receive({payload:-2}); - switchNode1.receive({payload:2}); - }); - }); - - it('should check input against a previous value (2nd option)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t": "btwn", "v": "10", "vt": "num", "v2": "", "v2t": "prev" }],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var c = 0; - helperNode1.on("input", function(msg) { - if (msg.payload) { - try { - if (c === 0) { - msg.payload.should.equal(20); - } - if (c === 1) { - msg.payload.should.equal(25); - done(); - } - c += 1; - } catch (err) { - done(err); - } - } else { - //done(); - } - }); - switchNode1.receive({payload:0}); - switchNode1.receive({payload:20}); // between 10 and 0 - switchNode1.receive({payload:30}); // between 10 and 20 - switchNode1.receive({payload:20}); // between 10 and 30 yes - switchNode1.receive({payload:30}); // between 10 and 20 no - switchNode1.receive({payload:25}); // between 10 and 30 yes - }); - }); - - it('should check if input is indeed null', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"null"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - if (msg.payload === null) { - done(); - } else { - console.log("msg is ",msg); - } - }); - switchNode1.receive({payload:null}); - }); - }); - - it('should check if input is indeed undefined', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"null"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - if (msg.payload === undefined) { - done(); - } - else { - console.log("msg is ",msg); - } - }); - switchNode1.receive({payload:undefined}); - }); - }); - it('should treat non-existant msg property conditional as undefined', function(done) { - var flow = [{"id":"switchNode1","type":"switch","z":"feee1df.c3263e","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"this.does.not.exist","vt":"msg"}],"checkall":"true","outputs":1,"x":190,"y":440,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var received = []; - helperNode1.on("input", function(msg) { - received.push(msg); - }); - // First message should be dropped as payload is not undefined - switchNode1.receive({topic:"messageOne",payload:""}); - // Second message should pass through as payload is undefined - switchNode1.receive({topic:"messageTwo",payload:undefined}); - setTimeout(function() { - try { - received.should.have.lengthOf(1); - received[0].should.have.a.property("topic","messageTwo"); - done(); - } catch(err) { - done(err); - } - },500) - }); - }); - - it('should check if input is indeed not null', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"nnull"}],checkall:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - if (msg.payload) { - done(); - } else { - try { - msg.payload.should.equal("Anything here"); - } catch (err) { - done(err); - } - } - }); - switchNode1.receive({payload:"Anything here"}); - }); - }); - - it('sends a message when the "else/otherwise" statement is selected' , function(done) { - singularSwitchTest("else", true, true, 123456, done); - }); - - it('handles more than one switch statement' , function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"eq","v":"Hello"},{"t":"cont","v":"ello"}, {"t":"else"}],checkall:true,outputs:3,wires:[["helperNode1"], ["helperNode2"], ["helperNode3"]]}, - {id:"helperNode1", type:"helper", wires:[]}, - {id:"helperNode2", type:"helper", wires:[]}, - {id:"helperNode3", type:"helper", wires:[]}]; - - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var helperNode2 = helper.getNode("helperNode2"); - var helperNode3 = helper.getNode("helperNode3"); - - var nodeHitCount = 0; - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello"); - nodeHitCount++; - } catch (err) { - done(err); - } - }); - helperNode2.on("input", function(msg) { - try { - msg.payload.should.equal("Hello"); - nodeHitCount++; - if (nodeHitCount == 2) { - done(); - } else { - try { - should.fail(null, null, "Both statements should be triggered!"); - } catch (err) { - done(err); - } - } - } catch (err) { - done(err); - } - }); - helperNode3.on("input", function(msg) { - try { - should.fail(null, null, "The otherwise/else statement should not be triggered here!"); - } catch (err) { - done(err); - } - }); - switchNode1.receive({payload:"Hello"}); - }); - }); - - it('stops after first statement' , function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"eq","v":"Hello"},{"t":"cont","v":"ello"}, {"t":"else"}],checkall:"false",outputs:3,wires:[["helperNode1"], ["helperNode2"], ["helperNode3"]]}, - {id:"helperNode1", type:"helper", wires:[]}, - {id:"helperNode2", type:"helper", wires:[]}, - {id:"helperNode3", type:"helper", wires:[]}]; - - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var helperNode2 = helper.getNode("helperNode2"); - var helperNode3 = helper.getNode("helperNode3"); - - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello"); - done(); - } catch (err) { - done(err); - } - }); - helperNode2.on("input", function(msg) { - try { - should.fail(null, null, "The otherwise/else statement should not be triggered here!"); - } catch (err) { - done(err); - } - }); - helperNode3.on("input", function(msg) { - try { - should.fail(null, null, "The otherwise/else statement should not be triggered here!"); - } catch (err) { - done(err); - } - }); - switchNode1.receive({payload:"Hello"}); - }); - }); - - it('should handle JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs(payload)",propertyType:"jsonata",rules:[{"t":"btwn","v":"$sqrt(16)","vt":"jsonata","v2":"$sqrt(36)","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, true, -5, done); - }); - - it('should handle flow and global contexts with JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs($flowContext(\"payload\"))",propertyType:"jsonata",rules:[{"t":"btwn","v":"$flowContext(\"vt\")","vt":"jsonata","v2":"$globalContext(\"v2t\")","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]],z:"flow"}, - {id:"helperNode1", type:"helper", wires:[],z:"flow"}, - {id:"flow",type:"tab"}]; - helper.load(switchNode, flow, function() { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - switchNode1.context().flow.set("payload",-5); - switchNode1.context().flow.set("vt",4); - switchNode1.context().global.set("v2t",6); - helperNode1.on("input", function(msg) { - try { - should.equal(msg.payload,"pass"); - done(); - } catch(err) { - done(err); - } - }); - switchNode1.receive({payload:"pass"}); - }); - }); - - it('should handle persistable flow and global contexts with JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$abs($flowContext(\"payload\",\"memory1\"))",propertyType:"jsonata",rules:[{"t":"btwn","v":"$flowContext(\"vt\",\"memory1\")","vt":"jsonata","v2":"$globalContext(\"v2t\",\"memory1\")","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]],z:"flow"}, - {id:"helperNode1", type:"helper", wires:[],z:"flow"}, - {id:"flow",type:"tab"}]; - helper.load(switchNode, flow, function() { - initContext(function () { - var switchNode1 = helper.getNode("switchNode1"); - var helperNode1 = helper.getNode("helperNode1"); - switchNode1.context().flow.set(["payload","vt"],[-7,6],"memory1",function(){ - switchNode1.context().global.set("v2t",8,"memory1",function(){ - helperNode1.on("input", function(msg) { - try { - should.equal(msg.payload,"pass"); - done(); - } catch(err) { - done(err); - } - }); - switchNode1.receive({payload:"pass"}); - }); - }); - }); - }); - }); - - it('should handle env var expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"VAR",propertyType:"env",rules:[{"t":"eq","v":"VAL"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - process.env.VAR = "VAL"; - customFlowSwitchTest(flow, true, "OK", done); - }); - - - it('should take head of message sequence (no repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":3}],checkall:false,repair:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], false, undefined, done); - }); - - it('should take head of message sequence (repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":3}],checkall:false,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], true, undefined, done); - }); - - it('should take head of message sequence (w. context)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":"count",vt:"global"}],checkall:false,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], true, - function(node) { - node.context().global.set("count", 3); - }, done); - }); - - it('should take head of message sequence (w. JSONata)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"head","v":"1+4/2",vt:"jsonata"}],checkall:false,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [0, 1, 2], true, undefined, done); - }); - - it('should take tail of message sequence (no repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"tail","v":3}],checkall:true,repair:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [2, 3, 4], false, undefined, done); - }); - - it('should take tail of message sequence (repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"tail","v":3}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [2, 3, 4], true, undefined, done); - }); - - it('should take slice of message sequence (no repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"index","v":1,"v2":3}],checkall:true,repair:false,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [1, 2, 3], false, undefined, done); - }); - - it('should take slice of message sequence (repair)', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"index","v":1,"v2":3}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [1, 2, 3], true, undefined, done); - }); - - it('should check JSONata expression is true', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"jsonata_exp","v":"payload%2 = 1","vt":"jsonata"}], - checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSwitchTest(flow, true, 9, done); - }); - - it('should be able to use $I in JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"jsonata_exp","v":"$I % 2 = 1",vt:"jsonata"}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [1, 3], true, undefined, done); - }); - - it('should be able to use $N in JSONata expression', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"jsonata_exp","v":"payload >= $N-2",vt:"jsonata"}],checkall:true,repair:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - customFlowSequenceSwitchTest(flow, [0, 1, 2, 3, 4], [3, 4], true, undefined, done); - }); - - - function customFlowSequenceMultiSwitchTest(flow, seq_in, outs, repair, done) { - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("n1"); - var port_count = Object.keys(outs).length; - var sid; - var ids = new Array(port_count).fill(undefined); - var counts = new Array(port_count).fill(0); - var vals = new Array(port_count); - var recv_count = 0; - for (var id in outs) { - if (outs.hasOwnProperty(id)) { - var out = outs[id]; - vals[out.port] = out.vals; - recv_count += out.vals.length; - } - } - var count = 0; - function check_msg(msg, ix, vf) { - try { - msg.should.have.property("payload"); - var payload = msg.payload; - msg.should.have.property("parts"); - vf(payload).should.be.ok(); - var parts = msg.parts; - var evals = vals[ix]; - parts.should.have.property("id"); - var id = parts.id; - if (repair) { - if (ids[ix] === undefined) { - ids[ix] = id; - } - else { - ids[ix].should.equal(id); - } - parts.should.have.property("count", evals.length); - parts.should.have.property("index", counts[ix]); - } - else { - if (sid === undefined) { - sid = id; - } - else { - sid.should.equal(id); - } - parts.should.have.property("count", seq_in.length); - parts.should.have.property("index", msg.xindex); - } - var index = parts.index; - var eindex = counts[ix]; - var value = evals[eindex]; - payload.should.equal(value); - counts[ix]++; - count++; - if (count === recv_count) { - done(); - } - } - catch (e) { - done(e); - } - } - for (var id in outs) { - if (outs.hasOwnProperty(id)) { - (function() { - var node = helper.getNode(id); - var port = outs[id].port; - var vf = outs[id].vf; - node.on("input", function(msg) { - check_msg(msg, port, vf); - }); - })(); - } - } - for(var i in seq_in) { - if (seq_in.hasOwnProperty(i)) { - n1.receive({payload:seq_in[i], xindex:i, - parts:{index:i, count:seq_in.length, id:222}}); - } - } - }); - } - - it('should not repair message sequence for each port', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"gt","v":0},{"t":"lt","v":0},{"t":"else"}], - checkall:true,repair:false, - outputs:3,wires:[["n2"],["n3"],["n4"]]}, - {id:"n2", type:"helper", wires:[]}, - {id:"n3", type:"helper", wires:[]}, - {id:"n4", type:"helper", wires:[]} - ]; - var data = [ 1, -2, 2, 0, -1 ]; - var outs = { - "n2" : { port:0, vals:[1, 2], - vf:function(x) { return(x > 0); } }, - "n3" : { port:1, vals:[-2, -1], - vf:function(x) { return(x < 0); } }, - "n4" : { port:2, vals:[0], - vf:function(x) { return(x == 0); } }, - }; - customFlowSequenceMultiSwitchTest(flow, data, outs, false, done); - }); - - it('should repair message sequence for each port', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"gt","v":0},{"t":"lt","v":0},{"t":"else"}], - checkall:true,repair:true, - outputs:3,wires:[["n2"],["n3"],["n4"]]}, - {id:"n2", type:"helper", wires:[]}, // >0 - {id:"n3", type:"helper", wires:[]}, // <0 - {id:"n4", type:"helper", wires:[]} // ==0 - ]; - var data = [ 1, -2, 2, 0, -1 ]; - var outs = { - "n2" : { port:0, vals:[1, 2], - vf:function(x) { return(x > 0); } }, - "n3" : { port:1, vals:[-2, -1], - vf:function(x) { return(x < 0); } }, - "n4" : { port:2, vals:[0], - vf:function(x) { return(x == 0); } }, - }; - customFlowSequenceMultiSwitchTest(flow, data, outs, true, done); - }); - - it('should repair message sequence for each port (overlap)', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"gte","v":0},{"t":"lte","v":0},{"t":"else"}], - checkall:true,repair:true, - outputs:3,wires:[["n2"],["n3"],["n4"]]}, - {id:"n2", type:"helper", wires:[]}, // >=0 - {id:"n3", type:"helper", wires:[]}, // <=0 - {id:"n4", type:"helper", wires:[]} // none - ]; - var data = [ 1, -2, 2, 0, -1 ]; - var outs = { - "n2" : { port:0, vals:[1, 2, 0], - vf:function(x) { return(x >= 0); } }, - "n3" : { port:1, vals:[-2, 0, -1], - vf:function(x) { return(x <= 0); } }, - "n4" : { port:2, vals:[], - vf:function(x) { return(false); } }, - }; - customFlowSequenceMultiSwitchTest(flow, data, outs, true, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1",type:"switch",name:"switchNode",property:"payload", - rules:[{"t":"tail","v":2}], - checkall:true,repair:false, - outputs:3,wires:[["n2"]]}, - {id:"n2", type:"helper", wires:[]} - ]; - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("n1"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "switch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "switch"); - evt.should.have.property('msg', "switch.errors.too-many"); - done(); - }, 150); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should handle invalid jsonata expression', function(done) { - - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"$invalidExpression(payload)",propertyType:"jsonata",rules:[{"t":"btwn","v":"$sqrt(16)","vt":"jsonata","v2":"$sqrt(36)","v2t":"jsonata"}],checkall:true,outputs:1,wires:[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("switchNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "switch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "switchNode1"); - evt.should.have.property('type', "switch"); - done(); - }, 150); - n1.receive({payload:1}); - }); - }); - - - it('should handle empty rule', function(done) { - var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[],checkall:true,outputs:0,wires:[]}]; - helper.load(switchNode, flow, function() { - var n1 = helper.getNode("switchNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "switch"; - }); - if (logEvents.length === 0) { - done(); - } - }, 150); - n1.receive({payload:1}); - }); - }); -}); diff --git a/test/nodes/core/function/15-change_spec.js b/test/nodes/core/function/15-change_spec.js deleted file mode 100644 index b8d7be03b..000000000 --- a/test/nodes/core/function/15-change_spec.js +++ /dev/null @@ -1,1911 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var changeNode = require("nr-test-utils").require("@node-red/nodes/core/function/15-change.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); - -describe('change Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - helper.stopServer(done); - }); - }); - - it('should load node with defaults', function(done) { - var flow = [{ id: "c1", type: "change", name:"change1" }]; - helper.load(changeNode, flow, function() { - helper.getNode("c1").should.have.property("name", "change1"); - helper.getNode("c1").should.have.property("rules", [{fromt:'str',pt:'msg',tot:'str',t:undefined,p:''}]); - done(); - }); - }); - it('should load defaults if set to replace', function(done) { - var flow = [{ id: "c1", type: "change", name:"change1", action:"replace" }]; - helper.load(changeNode, flow, function() { - helper.getNode("c1").should.have.property("name", "change1"); - helper.getNode("c1").should.have.property("rules", [ {fromt: 'str', p: '', pt: 'msg', t: 'set', to: '', tot: 'str'} ]); - done(); - }); - }); - it('should load defaults if set to change', function(done) { - var flow = [{ id: "c1", type: "change", name:"change1", action:"change" }]; - helper.load(changeNode, flow, function() { - //console.log(helper.getNode("c1")); - helper.getNode("c1").should.have.property("name", "change1"); - helper.getNode("c1").should.have.property("rules", [ { from: '', fromRE:/(?:)/g,fromt: 'str', p: '',pt: 'msg', re: undefined, t: 'change', to: '',tot: 'str' } ]); - done(); - }); - }); - it('should no-op if there are no rules', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[],"action":"","property":"","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.eql(sentMsg); - done(); - } catch(err) { - done(err); - } - }); - var sentMsg = {payload:"leaveMeAlong"}; - changeNode1.receive(sentMsg); - }); - }); - - describe('#set' , function() { - - it('sets the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"changed","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("changed"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"changeMe"}); - }); - }); - - it('sets the value of global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t":"set","p":"globalValue","pt":"global","to":"changed","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.get("globalValue").should.equal("changed"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","changeMe"); - changeNode1.receive({payload:""}); - }); - }); - - it('sets the value of persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t":"set","p":"#:(memory1)::globalValue","pt":"global","to":"changed","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.get("globalValue", "memory1", function (err, val) { - val.should.equal("changed"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","changeMe","memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('sets the value and type of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "set", "p": "payload", "pt": "msg", "to": "12345", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(12345); - var t = typeof(msg.payload); - t.should.equal("number"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"changeMe"}); - }); - }); - - it('sets the value of an already set multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"bar","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({foo:{bar:"foo"}}); - }); - }); - - it('sets the value of an empty multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"bar","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({}); - }); - }); - - it('sets the value of a message property to another message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo","from":"","to":"msg.fred","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var rule = helper.getNode("changeNode1").rules[0]; - rule.t.should.eql('set'); - rule.tot.should.eql('msg'); - helperNode1.on("input", function(msg) { - try { - msg.foo.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({fred:"bar"}); - }); - }); - - it('sets the value of a multi-level message property to another multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"msg.fred.red","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({fred:{red:"bar"}}); - }); - }); - - it('doesn\'t set the value of a message property when the \'to\' message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"foo.bar","from":"","to":"msg.fred.red","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - should.not.exist(msg.foo); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({}); - }); - }); - - it('overrides the value of a message property when the \'to\' message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.foo","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - should.not.exist(msg.payload); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello"}); - }); - }); - - it('sets the message property to null when the \'to\' message property equals null', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.foo","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - (msg.payload === null).should.be.true(); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello", foo:null}); - }); - }); - - it('does not set other properties using = inside to property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"payload","from":"","to":"msg.otherProp=10","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - should.not.exist(msg.payload); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"changeMe"}); - }); - }); - - it('splits dot delimited properties into objects', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"replace","property":"pay.load","from":"","to":"10","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.pay.load.should.equal("10"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({pay:{load:"changeMe"}}); - }); - }); - - it('changes the value to flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"flowValue","tot":"flow"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("flowValue","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to persistable flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"#:(memory1)::flowValue","tot":"flow"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("flowValue","Hello World!","memory1",function(err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value to global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"globalValue","tot":"global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"#:(memory1)::globalValue","tot":"global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!","memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value to a number', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"123","tot":"num"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(123); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to a boolean value', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"true","tot":"bool"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(true); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to a js object', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":'{"a":123}',"tot":"json"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql({a:123}); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value to a buffer object', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"[72,101,108,108,111,32,87,111,114,108,100]","tot":"bin"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var buff = Buffer.from("Hello World"); - msg.payload.should.eql(buff); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:""}); - }); - }); - - it('sets the value of the message property to the current timestamp', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"set","p":"ts","pt":"msg","to":"","tot":"date"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - (Date.now() - msg.ts).should.be.approximately(0,50); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:Date.now()}); - }); - }); - - describe('env var', function() { - before(function() { - process.env.NR_TEST_A = 'foo'; - }) - after(function() { - delete process.env.NR_TEST_A; - }) - it('sets the value using env property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("foo"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - it('sets the value using env property from tab', function(done) { - var flow = [ - {"id":"tab1","type":"tab","env":[ - {"name":"NR_TEST_A", "value":"bar", "type": "str"} - ]}, - {"id":"changeNode1","type":"change","z":"tab1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]} - ]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - it('sets the value using env property from group', function(done) { - var flow = [ - {"id":"group1","type":"group","env":[ - {"name":"NR_TEST_A", "value":"bar", "type": "str"} - ]}, - {"id":"changeNode1","type":"change","g":"group1",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]} - ]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - it('sets the value using env property from nested group', function(done) { - var flow = [ - {"id":"group1","type":"group","env":[ - {"name":"NR_TEST_A", "value":"bar", "type": "str"} - ]}, - {"id":"group2","type":"group","g":"group1","env":[]}, - {"id":"changeNode1","type":"change","g":"group2",rules:[{"t":"set","p":"payload","pt":"msg","to":"NR_TEST_A","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]} - ]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123",topic:"ABC"}); - }); - }); - - }); - - it('changes the value using jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$length(payload)","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(12); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('reports invalid jsonata expression', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$invalid(payload)","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - done("Invalid jsonata expression passed message through"); - }); - changeNode1.on("call:error", function(err) { - // Expect error to be called - done(); - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value using flow context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$flowContext(\"foo\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - changeNode1.context().flow.set("foo","bar"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value using global context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$globalContext(\"foo\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - changeNode1.context().global.set("foo","bar"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value using persistable flow context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$flowContext(\"foo\",\"memory1\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("foo","bar","memory1",function(err){ - if(err){ - done(err); - }else{ - changeNode1.context().flow.set("foo","error!"); - changeNode1.receive({payload:"Hello World!"}); - } - }); - }); - }); - }); - - it('changes the value using persistable global context with jsonata', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"set","p":"payload","to":"$globalContext(\"foo\",\"memory1\")","tot":"jsonata"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"},{"id":"flow","type":"tab"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("foo","bar","memory1",function(err){ - if(err){ - done(err); - }else{ - changeNode1.context().global.set("foo","error!"); - changeNode1.receive({payload:"Hello World!"}); - } - }); - }); - }); - }); - - it('sets the value of a message property using a nested property', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(2); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"",lookup:{a:1,b:2},topic:"b"}); - }); - }); - - it('sets the value of a nested message property using a message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"lookup[msg.topic]","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.lookup.b.should.equal("newValue"); - done(); - } catch(err) { - done(err); - } - }); - var msg = { - payload: "newValue", - lookup:{a:1,b:2}, - topic:"b" - } - changeNode1.receive(msg); - }); - }); - - it('sets the value of a message property using a nested property in flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(2); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("lookup",{a:1, b:2}); - changeNode1.receive({payload: "", topic: "b"}); - }); - }) - - it('sets the value of a message property using a nested property in flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"lookup[msg.topic]","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql(2); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("lookup",{a:1, b:2}); - changeNode1.receive({payload: "", topic: "b"}); - }); - }) - - it('sets the value of a nested flow context property using a message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","name":"","rules":[{"t":"set","p":"lookup[msg.topic]","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.eql("newValue"); - changeNode1.context().flow.get("lookup.b").should.eql("newValue"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("lookup",{a:1, b:2}); - changeNode1.receive({payload: "newValue", topic: "b"}); - }); - }) - - it('deep copies the property if selected', function(done) { - - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"set","p":"payload","pt":"msg","to":"source","tot":"msg","dc":true}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - // Check payload has been set to a clone of original object - // - the JSON should match - JSON.stringify(msg.payload).should.equal(JSON.stringify(originalObject)) - // - but they must be different objects - msg.payload.should.not.equal(originalObject); - - // Modify nested property of original object - originalObject.a.c = 3; - // Check that modification hasn't happened on cloned prop - msg.payload.a.should.not.have.property('c'); - - done(); - } catch(err) { - done(err); - } - }); - var originalObject = { a: { b: 2 } } - changeNode1.receive({source:originalObject}); - }); - - }) - }); - describe('#change', function() { - it('changes the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Goodbye World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value and doesnt change type of the message property for partial match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "msg", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Change456Me"); - var t = typeof(msg.payload); - t.should.equal("string"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Change123Me"}); - }); - }); - - it('changes the value and type of the message property if a complete match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "msg", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(456); - var t = typeof(msg.payload); - t.should.equal("number"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123"}); - }); - }); - - it('changes the value of a multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo.bar","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.foo.bar.should.equal("Goodbye World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({foo:{bar:"Hello World!"}}); - }); - }); - - it('sends unaltered message if the changed message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('sends unaltered message if a changed multi-level message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"foo.bar","from":"Hello","to":"Goodbye","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World!"}); - }); - }); - - it('changes the value of the message property based on a regex', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\d+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Replace all numbers NUMBER and NUMBER"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Replace all numbers 12 and 14"}); - }); - }); - - it('supports regex groups', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"(Hello)","to":"$1-$1-$1","reg":true,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello-Hello-Hello World"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World"}); - }); - }); - - it('reports invalid regex', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"change","property":"payload","from":"\\+**+","to":"NUMBER","reg":true,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "change"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'changeNode1'); - done(); - - }); - }); - - it('supports regex groups - new rule format', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"(Hello)","to":"$1-$1-$1","fromt":"re","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("Hello-Hello-Hello World"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"Hello World"}); - }); - }); - - it('changes the value - new rule format', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"ABC","to":"123","fromt":"str","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - - it('changes the value using msg property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"msg","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"abcABCabc",topic:"ABC"}); - }); - }); - - it('changes the value using flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"flow","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("topic","ABC"); - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - - it('changes the value using persistable flow context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"#:(memory1)::topic","to":"123","fromt":"flow","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("topic","ABC","memory1", function (err) { - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - }); - }); - - it('changes the value using global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"123","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic","ABC"); - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - - it('changes the value using persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"#:(memory1)::topic","to":"123","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc123abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic","ABC","memory1",function (err) { - changeNode1.receive({payload:"abcABCabc"}); - }); - }); - }); - }); - - it('changes the number using global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"ABC","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("ABC"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic",123); - changeNode1.receive({payload:123}); - }); - }); - - it('changes the number using persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"#:(memory1)::topic","to":"ABC","fromt":"global","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("ABC"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("topic",123,"memory1",function (err) { - changeNode1.receive({payload:123}); - }); - }); - }); - }); - - it('changes the value using number - string payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"123","to":"456","fromt":"num","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("456"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"123"}); - }); - }); - - it('changes the value using number - number payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"123","to":"abc","fromt":"num","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:123}); - }); - }); - - it('changes the value using boolean - string payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"true","to":"xxx","fromt":"bool","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("xxx"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"true"}); - }); - }); - - it('changes the value using boolean - boolean payload', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"true","to":"xxx","fromt":"bool","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("xxx"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:true}); - }); - }); - - it('changes the value of the global context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "global", "from": "Hello", "fromt": "str", "to": "Goodbye", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().global.get("payload").should.equal("Goodbye World!"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("payload","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value of the persistable global context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "global", "from": "Hello", "fromt": "str", "to": "Goodbye", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().global.get("payload","memory1", function (err, val) { - val.should.equal("Goodbye World!"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("payload","Hello World!","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value and doesnt change type of the flow context for partial match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal("Change456Me"); - helperNode1.context().flow.get("payload").should.be.a.String(); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","Change123Me"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value and doesnt change type of the persistable flow context for partial match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.equal("Change456Me"); - val.should.be.a.String(); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","Change123Me","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value and type of the flow context if a complete match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal(456); - helperNode1.context().flow.get("payload").should.be.a.Number(); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","123"); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value and type of the persistable flow context if a complete match', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "123", "fromt": "str", "to": "456", "tot": "num" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.be.a.Number(); - val.should.equal(456); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload","123","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value using number - number flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "123", "fromt": "num", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal("abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",123); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value using number - number persistable flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "123", "fromt": "num", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.equal("abc"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",123,"memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('changes the value using boolean - boolean flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "payload", "pt": "flow", "from": "true", "fromt": "bool", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload").should.equal("abc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",true); - changeNode1.receive({payload:""}); - }); - }); - - it('changes the value using boolean - boolean persistable flow context', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "change", "p": "#:(memory1)::payload", "pt": "flow", "from": "true", "fromt": "bool", "to": "abc", "tot": "str" }],"reg":false,"name":"changeNode","wires":[["helperNode1"]],"z":"flow"}, - {id:"helperNode1", type:"helper", wires:[],"z":"flow"}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - helperNode1.context().flow.get("payload","memory1",function (err, val) { - val.should.equal("abc"); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().flow.set("payload",true,"memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('reports invalid fromValue', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"null","fromt":"msg","to":"abc","tot":"str"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "change"; - }); - logEvents.should.have.length(1); - var msg = logEvents[0][0]; - msg.should.have.property('level', helper.log().ERROR); - msg.should.have.property('id', 'changeNode1'); - done(); - },25); - changeNode1.receive({payload:"",null:null}); - }); - }); - - describe('env var', function() { - before(function() { - process.env.NR_TEST_A = 'foo'; - }) - after(function() { - delete process.env.NR_TEST_A; - }) - it('changes the value using env property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{"t":"change","p":"payload","from":"topic","to":"NR_TEST_A","fromt":"msg","tot":"env"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("abcfooabc"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"abcABCabc",topic:"ABC"}); - }); - }); - }); - - }); - - describe("#delete", function() { - it('deletes the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"payload","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('payload'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"This won't get through!"}); - }); - }); - - it('deletes the value of global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "delete", "p": "globalValue", "pt": "global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.should.not.have.property("globalValue"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!"); - changeNode1.receive({payload:""}); - }); - }); - - it('deletes the value of persistable global context property', function(done) { - var flow = [{"id":"changeNode1","type":"change",rules:[{ "t": "delete", "p": "#:(memory1)::globalValue", "pt": "global"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - changeNode1.context().global.get("globalValue","memory1",function(err,val) { - should.equal(undefined); - done(); - }); - } catch(err) { - done(err); - } - }); - changeNode1.context().global.set("globalValue","Hello World!","memory1",function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - - it('deletes the value of a multi-level message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo.bar","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('foo.bar'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"This won't get through!", foo:{bar:"This will be deleted!"}}); - }); - }); - - it('sends unaltered message if the deleted message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('foo'); - msg.payload.should.equal('payload'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"payload"}); - }); - }); - - it('sends unaltered message if a deleted multi-level message property does not exist', function(done) { - var flow = [{"id":"changeNode1","type":"change","action":"delete","property":"foo.bar","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('foo.bar'); - msg.payload.should.equal('payload'); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"payload"}); - }); - }); - }); - - describe("#move", function() { - it('moves the value of the message property', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('topic'); - msg.should.have.property('payload'); - msg.payload.should.equal("You've got to move it move it."); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({topic:"You've got to move it move it.", payload:{foo:"bar"}}); - }); - }); - it('moves the value of a message property object', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"topic","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('topic'); - msg.should.have.property('payload'); - msg.payload.should.have.property('foo'); - msg.payload.foo.should.have.property('bar'); - msg.payload.foo.bar.should.equal(1); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({topic:{foo:{bar:1}}, payload:"String"}); - }); - }); - it('moves the value of a message property object to a sub-property', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.foo","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.have.property('foo'); - msg.payload.foo.should.equal("bar"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:"bar"}); - }); - }); - it('moves the value of a message sub-property object to a property', function(done) { - var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload.foo","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.equal("bar"); - (typeof msg.payload).should.equal("string"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({payload:{foo:"bar"}}); - }); - }); - }); - - describe('- multiple rules', function() { - it('handles multiple rules', function(done) { - var flow = [{"id":"changeNode1","type":"change","wires":[["helperNode1"]], - rules:[ - {t:"set",p:"payload",to:"newValue"}, - {t:"change",p:"changeProperty",from:"this",to:"that"}, - {t:"delete",p:"deleteProperty"} - ]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("newValue"); - msg.changeProperty.should.equal("change that value"); - should.not.exist(msg.deleteProperty); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({ - payload:"changeMe", - changeProperty:"change this value", - deleteProperty:"delete this value" - }); - }); - }); - - it('applies multiple rules in order', function(done) { - var flow = [{"id":"changeNode1","type":"change","wires":[["helperNode1"]], - rules:[ - {t:"set",p:"payload",to:"a this (hi)"}, - {t:"change",p:"payload",from:"this",to:"that"}, - {t:"change",p:"payload",from:"\\(.*\\)",to:"[new]",re:true}, - ]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal("a that [new]"); - done(); - } catch(err) { - done(err); - } - }); - changeNode1.receive({ - payload:"changeMe" - }); - }); - }); - - it('can access two persistable flow context property', function(done) { - var flow = [{"id":"changeNode1", "z":"t1", "type":"change", - "wires":[["helperNode1"]], - rules:[ - {"t":"set", "p":"val0", "to":"#:(memory0)::val", "tot":"flow"}, - {"t":"set", "p":"val1", "to":"#:(memory1)::val", "tot":"flow"} - ]}, - {id:"helperNode1", "z":"t1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("val0", "foo"); - msg.should.have.property("val1", "bar"); - done(); - } catch(err) { - done(err); - } - }); - var flow = changeNode1.context().flow; - flow.set("val", "foo", "memory0", function (err) { - flow.set("val", "bar", "memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - }); - - it('can access two persistable global context property', function(done) { - var flow = [{"id":"changeNode1", "z":"t1", "type":"change", - "wires":[["helperNode1"]], - rules:[ - {"t":"set", "p":"val0", "to":"#:(memory0)::val", "tot":"global"}, - {"t":"set", "p":"val1", "to":"#:(memory1)::val", "tot":"global"} - ]}, - {id:"helperNode1", "z":"t1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("val0", "foo"); - msg.should.have.property("val1", "bar"); - done(); - } catch(err) { - done(err); - } - }); - var global = changeNode1.context().global; - global.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - }); - - it('can access persistable global & flow context property', function(done) { - var flow = [{"id":"changeNode1", "z":"t1", "type":"change", - "wires":[["helperNode1"]], - rules:[ - {"t":"set", "p":"val0", "to":"#:(memory0)::val", "tot":"flow"}, - {"t":"set", "p":"val1", "to":"#:(memory1)::val", "tot":"global"} - ]}, - {id:"helperNode1", "z":"t1", type:"helper", wires:[]}]; - helper.load(changeNode, flow, function() { - initContext(function () { - var changeNode1 = helper.getNode("changeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.have.property("val0", "foo"); - msg.should.have.property("val1", "bar"); - done(); - } catch(err) { - done(err); - } - }); - var context = changeNode1.context(); - var flow = context.flow; - var global = context.global; - flow.set("val", "foo", "memory0", function (err) { - global.set("val", "bar", "memory1", function (err) { - changeNode1.receive({payload:""}); - }); - }); - }); - }); - }); - - }); -}); diff --git a/test/nodes/core/function/16-range_spec.js b/test/nodes/core/function/16-range_spec.js deleted file mode 100644 index a0dcd0078..000000000 --- a/test/nodes/core/function/16-range_spec.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var rangeNode = require("nr-test-utils").require("@node-red/nodes/core/function/16-range.js"); -var helper = require("node-red-node-test-helper"); - -describe('range Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - helper.unload(); - helper.stopServer(done); - }); - - it('should load some defaults', function(done) { - var flow = [{"id":"rangeNode1","type":"range","name":"rangeNode"}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - rangeNode1.should.have.property('name', 'rangeNode'); - rangeNode1.should.have.property('round', false); - done(); - }); - }); - - /** - * Run a generic range test - * @param action - scale/clamp (range limit)/roll (modulo): what action to choose - * @param minin - map from minimum value - * @param maxin - map from maximum value - * @param minout - map to minimum value - * @param maxout - map to maximum value - * @param round - whether to round the result to the nearest integer - * @param aPayload - what payload to send to the range node - * @param expectedResult - what result we're expecting - * @param done - the callback to call when test done - */ - function genericRangeTest(action, minin, maxin, minout, maxout, round, aPayload, expectedResult, done) { - var flow = [{"id":"rangeNode1","type":"range","minin":minin,"maxin":maxin,"minout":minout,"maxout":maxout,"action":action,"round":round,"name":"rangeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.payload.should.equal(expectedResult); - done(); - } catch(err) { - done(err); - } - }); - rangeNode1.receive({payload:aPayload}); - }); - } - - it('ranges numbers up tenfold', function(done) { - genericRangeTest("scale", 0, 100, 0, 1000, false, 50, 500, done); - }); - - it('ranges numbers down such as centimetres to metres', function(done) { - genericRangeTest("scale", 0, 100, 0, 1, false, 55, 0.55, done); - }); - - it('wraps numbers down say for degree/rotation reading 1/2', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, 15, 180, done); // 1/2 around wrap => "one and a half turns" - }); - - it('wraps numbers around say for degree/rotation reading 1/3', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, 13.3333, 120, done); // 1/3 around wrap => "one and a third turns" - }); - - it('wraps numbers around say for degree/rotation reading 1/4', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, 12.5, 90, done); // 1/4 around wrap => "one and a quarter turns" - }); - - it('wraps numbers down say for degree/rotation reading 1/4', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, -12.5, 270, done); // 1/4 backwards wrap => "one and a quarter turns backwards" - }); - - it('wraps numbers around say for degree/rotation reading 0', function(done) { - genericRangeTest("roll", 0, 10, 0, 360, true, -10, 0, done); - }); - - it('clamps numbers within a range - over max', function(done) { - genericRangeTest("clamp", 0, 10, 0, 1000, false, 111, 1000, done); - }); - - it('clamps numbers within a range - below min', function(done) { - genericRangeTest("clamp", 0, 10, 0, 1000, false, -1, 0, done); - }); - - it('just passes on msg if payload not present', function(done) { - var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":100,"minout":0,"maxout":100,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - msg.should.not.have.property('payload'); - msg.topic.should.equal("pass on"); - done(); - } catch(err) { - done(err); - } - }); - rangeNode1.receive({topic:"pass on"}); - }); - }); - - it('reports if input is not a number', function(done) { - var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":0,"minout":0,"maxout":0,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(rangeNode, flow, function() { - var rangeNode1 = helper.getNode("rangeNode1"); - var helperNode1 = helper.getNode("helperNode1"); - rangeNode1.on("call:log",function(args) { - var log = args.args[0]; - if (log.indexOf("notnumber") > -1) { - rangeNode1.log.restore(); - done(); - } else { - try { - should.fail(null, null, "Non-number inputs should be reported!"); - } catch (err) { - rangeNode1.log.restore(); - done(err); - } - } - }); - - rangeNode1.receive({payload:"NOT A NUMBER"}); - }); - }); -}); diff --git a/test/nodes/core/function/80-template_spec.js b/test/nodes/core/function/80-template_spec.js deleted file mode 100644 index e944824b3..000000000 --- a/test/nodes/core/function/80-template_spec.js +++ /dev/null @@ -1,498 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var templateNode = require("nr-test-utils").require("@node-red/nodes/core/function/80-template.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var helper = require("node-red-node-test-helper"); - -describe('template node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - beforeEach(function(done) { - done(); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { // do not use (for excluding effect fallback) - module: "memory" - }, - memory1: { - module: "memory" - }, - memory2: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function() { - helper.unload().then(function () { - return Context.clean({allNodes:{}}); - }).then(function () { - return Context.close(); - }); - }); - - - it('should modify payload using node-configured template', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - msg.should.have.property('template', 'this should be ignored as the node has its own template {{payload}}'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar", template: "this should be ignored as the node has its own template {{payload}}"}); - }); - }); - - it('should modify the configured property using msg.template', function(done) { - var flow = [{id:"n1", type:"template", field:"randomProperty", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - msg.should.have.property('template', 'payload={{payload}}'); - msg.should.have.property('randomProperty', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo", topic: "bar", template: "payload={{payload}}"}); - }); - }); - - it('should be able to overwrite msg.template using the template from msg.template', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'topic=bar'); - msg.should.have.property('template', 'topic={{topic}}'); - done(); - }); - n1.receive({payload:"foo", topic: "bar", template: "topic={{topic}}"}); - }); - }); - - it('should modify payload from msg.template', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", template:"",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var received = []; - n2.on("input", function(msg) { - try { - received.push(msg); - if (received.length === 3) { - received[0].should.have.property('topic', 'bar'); - received[0].should.have.property('payload', 'topic=bar'); - received[0].should.have.property('template', 'topic={{topic}}'); - - received[1].should.have.property('topic', 'another bar'); - received[1].should.have.property('payload', 'topic=another bar'); - received[1].should.have.property('template', 'topic={{topic}}'); - - received[2].should.have.property('topic', 'bar'); - received[2].should.have.property('payload', 'payload=foo'); - received[2].should.have.property('template', 'payload={{payload}}'); - done(); - } - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", topic: "bar", template: "topic={{topic}}"}); - n1.receive({payload:"foo", topic: "another bar", template: "topic={{topic}}"}); - n1.receive({payload:"foo", topic: "bar", template: "payload={{payload}}"}); - }); - }); - - it('should modify payload from flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().flow.set("value","foo"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify payload from persistable flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow[memory1].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.context().flow.set("value","foo","memory1",function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - - it('should handle nested context tags - property not set', function(done) { - // This comes from the Coursera Node-RED course and is a good example of - // multiple conditional tags - var template = `{{#flow.time}}time={{flow.time}}{{/flow.time}}{{^flow.time}}!time{{/flow.time}}{{#flow.random}}random={{flow.random}}randomtime={{flow.randomtime}}{{/flow.random}}{{^flow.random}}!random{{/flow.random}}`; - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:template,wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', '!time!random'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }) - it('should handle nested context tags - property set', function(done) { - // This comes from the Coursera Node-RED course and is a good example of - // multiple conditional tags - var template = `{{#flow.time}}time={{flow.time}}{{/flow.time}}{{^flow.time}}!time{{/flow.time}}{{#flow.random}}random={{flow.random}}randomtime={{flow.randomtime}}{{/flow.random}}{{^flow.random}}!random{{/flow.random}}`; - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:template,wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'time=123random=456randomtime=789'); - done(); - } catch(err) { - done(err); - } - }); - n1.context().flow.set(["time","random","randomtime"],["123","456","789"],function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }) - - it('should modify payload from two persistable flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow[memory1].value}}/{{flow[memory2].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo/bar'); - done(); - }); - n1.context().flow.set("value","foo","memory1",function (err) { - n1.context().flow.set("value","bar","memory2",function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - }); - - it('should modify payload from global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context().global.set("value","foo"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify payload from persistable global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global[memory1].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.context().global.set("value","foo","memory1", function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - - it('should modify payload from two persistable global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global[memory1].value}}/{{global[memory2].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo/bar'); - done(); - }); - n1.context().global.set("value","foo","memory1", function (err) { - n1.context().global.set("value","bar","memory2", function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - }); - - it('should modify payload from persistable flow & global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow[memory1].value}}/{{global[memory1].value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo/bar'); - done(); - }); - n1.context().flow.set("value","foo","memory1", function (err) { - n1.context().global.set("value","bar","memory1", function (err) { - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - }); - }); - - it('should handle missing node context', function(done) { - // this is artificial test because in flow there is missing z property (probably never happen in real usage) - var flow = [{id:"n1",type:"template", field:"payload", template:"payload={{flow.value}},{{global.value}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=,'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle escape characters in Mustache format and JSON output mode', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", syntax:"mustache", template:"{\"data\":\"{{payload}}\"}", output:"json", wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.payload.should.have.property('data', 'line\t1\nline\\2\r\nline\b3\f'); - done(); - }); - n1.receive({payload:"line\t1\nline\\2\r\nline\b3\f"}); - }); - }); - - it('should modify payload in plain text mode', function(done) { - var flow = [{id:"n1", type:"template", field:"payload", syntax:"plain", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload={{payload}}'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in flow context - n2.context().flow.get("payload").should.equal("payload=foo"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify persistable flow context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"#:(memory1)::payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in flow context - n2.context().flow.get("payload", "memory1", function (err, val) { - val.should.equal("payload=foo"); - done(); - }); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should modify global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in global context - n2.context().global.get("payload").should.equal("payload=foo"); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should modify persistable global context', function(done) { - var flow = [{id:"n1",z:"t1", type:"template", field:"#:(memory1)::payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; - helper.load(templateNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - // mesage is intact - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'foo'); - // result is in global context - n2.context().global.get("payload", "memory1", function (err, val) { - val.should.equal("payload=foo"); - done(); - }); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - }); - - it('should handle if the field isn\'t set', function(done) { - var flow = [{id:"n1", type:"template", template: "payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload', 'payload=foo'); - done(); - }); - n1.receive({payload:"foo",topic: "bar"}); - }); - }); - - it('should handle deeper objects', function(done) { - var flow = [{id:"n1", type:"template", field: "topic.foo.bar", template: "payload={{payload.doh.rei.me}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic'); - msg.topic.should.have.property('foo'); - msg.topic.foo.should.have.a.property('bar', 'payload=foo'); - done(); - }); - n1.receive({payload:{doh:{rei:{me:"foo"}}}}); - }); - }); - - it('should handle block contexts objects', function(done) { - var flow = [{id:"n1", type:"template", template: "A{{#payload.A}}{{payload.A}}{{.}}{{/payload.A}}B",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload','AabcabcB'); - done(); - }); - n1.receive({payload:{A:"abc"}}); - }); - }); - - it('should raise error if passed bad template', function(done) { - var flow = [{id:"n1", type:"template", field: "payload", template: "payload={{payload",wires:[["n2"]]},{id:"n2",type:"helper"}]; - helper.load(templateNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "template"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Unclosed tag at "); - done(); - },25); - n1.receive({payload:"foo"}); - }); - }); - -}); diff --git a/test/nodes/core/function/89-delay_spec.js b/test/nodes/core/function/89-delay_spec.js deleted file mode 100644 index 17958b95b..000000000 --- a/test/nodes/core/function/89-delay_spec.js +++ /dev/null @@ -1,1004 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var delayNode = require("nr-test-utils").require("@node-red/nodes/core/function/89-delay.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); - -var GRACE_PERCENTAGE=10; - -var nanosToSeconds = 1000000000; -var millisToSeconds = 1000; - -var secondsToMinutes = 60; -var secondsToHours = 3600; -var secondsToDays = 86400; - -describe('delay Node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - RED.settings.nodeMessageBufferMaxLength = 0; - helper.unload(); - helper.stopServer(done); - }); - - it('should be loaded', function(done) { - var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"day","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - delayNode1.should.have.property('name', 'delayNode'); - delayNode1.should.have.property('rate', 86400000); - done(); - }); - }); - - it('should be able to set rate to hour', function(done) { - var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"hour","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - delayNode1.should.have.property('name', 'delayNode'); - delayNode1.should.have.property('rate', 3600000); - done(); - }); - }); - - it('should be able to set rate to minute', function(done) { - var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - delayNode1.should.have.property('name', 'delayNode'); - delayNode1.should.have.property('rate', 60000); - done(); - }); - }); - - var TimeUnitEnum = { - MILLIS : "milliseconds", - SECONDS : "seconds", - MINUTES : "minutes", - HOURS : "hours", - DAYS : "days" - } - - /** - * Tells whether two numeric values are close enough to each other - * @param actualValue - the value we're testing - * @param expectedValue - the value we're matching the test value against - * @param tolerancePercent - the percentage of tolerated deviation (0 means equals) - */ - function closeEnough(actualValue, expectedValue, tolerancePercent) { - var toReturn; - var toleranceFraction = expectedValue * (tolerancePercent/100); - var minExpected = expectedValue - toleranceFraction; - var maxExpected = expectedValue + toleranceFraction; - - if (actualValue >= minExpected && actualValue <= maxExpected) { - toReturn = true; - } else { - toReturn = false; - } - return toReturn; - } - - /** - * Runs a delay test - * @param aTimeout - the timeout quantity - * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days - */ - function genericDelayTest(aTimeout, aTimeoutUnit, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":aTimeout,"timeoutUnits":aTimeoutUnit,"rate":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var endTime = process.hrtime(startTime); - var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] ); - var runtimeSeconds = runtimeNanos / nanosToSeconds; - var aTimeoutUnifiedToSeconds; - - // calculating the timeout in seconds - if (aTimeoutUnit == TimeUnitEnum.MILLIS) { - aTimeoutUnifiedToSeconds = aTimeout / millisToSeconds; - } else if (aTimeoutUnit == TimeUnitEnum.SECONDS) { - aTimeoutUnifiedToSeconds = aTimeout; - } else if (aTimeoutUnit == TimeUnitEnum.MINUTES) { - aTimeoutUnifiedToSeconds = aTimeout * secondsToMinutes; - } else if (aTimeoutUnit == TimeUnitEnum.HOURS) { - aTimeoutUnifiedToSeconds = aTimeout * secondsToHours; - } else if (aTimeoutUnit == TimeUnitEnum.DAYS) { - aTimeoutUnifiedToSeconds = aTimeout * secondsToDays; - } - - if (closeEnough(runtimeSeconds, aTimeoutUnifiedToSeconds, GRACE_PERCENTAGE)) { - done(); - } else { - try { - should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not close enough to exlected timeout seconds: " + aTimeoutUnifiedToSeconds); - } catch (err) { - done(err); - } - } - } catch(err) { - done(err); - } - }); - var startTime = process.hrtime(); - delayNode1.receive({payload:"delayMe"}); - }); - } - - /** - * We send a message, take a timestamp then when the message is received by the helper node, we take another timestamp. - * Then check if the message has been delayed by the expected amount. - */ - - it('delays the message in seconds', function(done) { - genericDelayTest(0.5, "seconds", done); - }); - - it('delays the message in milliseconds', function(done) { - genericDelayTest(500, "milliseconds", done); - }); - - it('delays the message in minutes', function(done) { // this is also 0.5 seconds - genericDelayTest(0.00833, "minutes", done); - }); - - it('delays the message in hours', function(done) { // this is also 0.5 seconds - genericDelayTest(0.0001388, "hours", done); - }); - - it('delays the message in days', function(done) { // this is also 0.5 seconds - genericDelayTest(0.000005787, "days", done); - }); - - /** - * Runs a rate limit test - only testing seconds! - * @param aLimit - the message limit count - * @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds - * @param runtimeInMillis - when to terminate run and count messages received - */ - function genericRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, rateValue, done) { - var flow = [{"id":"delayNode1","type":"delay","nbRateUnits":nbUnit,"name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var receivedMessagesStack = []; - var rate = 1000 / aLimit * nbUnit; - - var receiveTimestamp; - - helperNode1.on("input", function(msg) { - if (receiveTimestamp) { - var elapse = process.hrtime(receiveTimestamp); - var receiveInterval = (elapse[0] * 1000) + ((elapse[1] / nanosToSeconds) * 1000); - receiveInterval.should.be.above(rate * 0.9); - } - receiveTimestamp = process.hrtime(); - receivedMessagesStack.push(msg); - }); - - var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst - - var i = 0; - for (; i < possibleMaxMessageCount + 1; i++) { - delayNode1.receive({ payload: i, rate: rateValue }); - } - - setTimeout(function() { - try { - receivedMessagesStack.length.should.be.lessThan(possibleMaxMessageCount); - for (var j = 0; j < receivedMessagesStack.length; j++) { - if (receivedMessagesStack[j].payload === j) { - if (j === (receivedMessagesStack.length -1)) { // last message, all matched so far - done(); - } - } else { - should.fail(null, null, "Received messages were not received in order. Message was " + receivedMessagesStack[i].payload + " on count " + i); - } - } - } catch (err) { - done(err); - } - }, runtimeInMillis); - }); - } - - it('limits the message rate to 1 per second', function(done) { - genericRateLimitSECONDSTest(1, 1, 1500, null, done); - }); - - it('limits the message rate to 1 per 2 seconds', function(done) { - this.timeout(6000); - genericRateLimitSECONDSTest(1, 2, 3000, null, done); - }); - - it('limits the message rate to 2 per seconds, 2 seconds', function(done) { - this.timeout(6000); - genericRateLimitSECONDSTest(2, 1, 2100, null, done); - }); - - it('limits the message rate using msg.rate', function (done) { - RED.settings.nodeMessageBufferMaxLength = 3; - genericRateLimitSECONDSTest(1, 1, 1500, 2000, done); - }); - - /** - * Runs a rate limit test with drop support - only testing seconds! - * @param aLimit - the message limit count - * @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds - * @param runtimeInMillis - when to terminate run and count messages received - */ - function dropRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, rateValue, sendIntermediate, done) { - if (!done) { - done = sendIntermediate; - sendIntermediate = false; - } - var outputs = 1; - if (sendIntermediate) { - outputs = 2; - } - var flow = [ - {"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"nbRateUnits":nbUnit,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,outputs:outputs,"wires":[["helperNode1"],["helperNode2"]]}, - {id:"helperNode1", type:"helper", wires:[]}, - {id:"helperNode2", type:"helper", wires:[]} - ] - - ; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var helperNode2 = helper.getNode("helperNode2"); - var receivedMessagesStack = []; - var receivedIntermediateMessagesStack = []; - - // Add a small grace to the calculated delay - var rate = 1000/aLimit + 10; - - var receiveTimestamp; - - helperNode1.on("input", function(msg) { - if (receiveTimestamp) { - var elapse = process.hrtime(receiveTimestamp); - var receiveInterval = (elapse[0] * 1000) + ((elapse[1] / nanosToSeconds) * 1000); - receiveInterval.should.be.above(rate * 0.9); - } - receiveTimestamp = process.hrtime(); - receivedMessagesStack.push(msg); - }); - helperNode2.on("input", function(msg) { - receivedIntermediateMessagesStack.push(msg); - }); - - var possibleMaxMessageCount = Math.ceil(aLimit * (runtimeInMillis / 1000) + aLimit); // +aLimit as at the start of the 2nd period, we're allowing the 3rd burst - - var i = 0; - delayNode1.receive({ payload: i, rate: rateValue }); - i++; - for (; i < possibleMaxMessageCount + 1; i++) { - setTimeout(function() { - delayNode1.receive({payload:i}); - }, 2 * ((rate * i) / possibleMaxMessageCount) ); - } - - //we need to send a message delayed so that it doesn't get dropped - setTimeout(function() { - delayNode1.receive({payload:++i}); - }, runtimeInMillis - 300); // should give enough time to squeeze another message in - - setTimeout(function() { - try { - receivedMessagesStack.length.should.be.lessThan(possibleMaxMessageCount + 1); - receivedMessagesStack.length.should.be.greaterThan(2); // ensure that we receive more than 1st and last message - receivedMessagesStack[0].payload.should.be.exactly(0); // means we received the last message injected just before test termination - var foundAtLeastOneDrop = false; - for (var i = 0; i < receivedMessagesStack.length; i++) { - if (i > 0) { - if (receivedMessagesStack[i].payload - receivedMessagesStack[i - 1].payload > 1) { - foundAtLeastOneDrop = true; - } - } - } - foundAtLeastOneDrop.should.be.true(); - if (sendIntermediate) { - receivedIntermediateMessagesStack.length.should.be.greaterThan(0); - } else { - receivedIntermediateMessagesStack.length.should.be.exactly(0); - } - done(); - } catch (err) { - done(err); - } - }, runtimeInMillis); - }); - } - - it('limits the message rate to 1 per second, 4 seconds, with drop', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(1, 1, 4000, null, done); - }); - - it('limits the message rate to 1 per 2 seconds, 4 seconds, with drop', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(1, 2, 4500, null, done); - }); - - it('limits the message rate to 2 per second, 5 seconds, with drop', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(2, 1, 5000, null, done); - }); - - it('limits the message rate to 2 per second, 5 seconds, with drop, 2nd output', function(done) { - this.timeout(6000); - dropRateLimitSECONDSTest(2, 1, 5000, null, true, done); - }); - - it('limits the message rate with drop using msg.rate', function (done) { - this.timeout(6000); - RED.settings.nodeMessageBufferMaxLength = 3; - dropRateLimitSECONDSTest(2, 1, 5000, 1000, done); - }); - - /** - * Returns true if the actualTimeout is gracefully in between the timeoutFrom and timeoutTo - * values. Gracefully means that inBetween could actually mean smaller/greater values - * than the timeout range so long as it's within an actual grace percentage. - * @param timeoutFrom - The expected timeout range (low number) - * @param timeoutTo - The expected timeout range (high number) - * @param actualTimeout - The actual measured timeout value of test - * @param allowedGracePercent - The percentage of grace allowed - */ - function inBetweenDelays(timeoutFrom, timeoutTo, actualTimeout, allowedGracePercent) { - if (closeEnough(actualTimeout, timeoutFrom, allowedGracePercent)) { - return true; - } else if (closeEnough(actualTimeout, timeoutTo, allowedGracePercent)) { - return true; - } else if (timeoutFrom < actualTimeout && timeoutTo > actualTimeout) { - return true; - } else { - return false; - } - } - - /** - * Runs a VARIABLE DELAY test, checks if the delay is in between the given timeout values - * @param aTimeoutFrom - the timeout quantity which is the minimal acceptable wait period - * @param aTimeoutTo - the timeout quantity which is the maximum acceptable wait period - * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days - * @param delay - the variable delay: milliseconds - */ - function variableDelayTest(aTimeoutFrom, aTimeoutTo, aTimeoutUnit, delay, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delayv","timeout":0.5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var endTime = process.hrtime(startTime); - var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] ); - var runtimeSeconds = runtimeNanos / nanosToSeconds; - var aTimeoutFromUnifiedToSeconds; - var aTimeoutToUnifiedToSeconds; - - // calculating the timeout in seconds - if (aTimeoutUnit == TimeUnitEnum.MILLIS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom / millisToSeconds; - aTimeoutToUnifiedToSeconds = aTimeoutTo / millisToSeconds; - } else if (aTimeoutUnit == TimeUnitEnum.SECONDS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom; - aTimeoutToUnifiedToSeconds = aTimeoutTo; - } else if (aTimeoutUnit == TimeUnitEnum.MINUTES) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToMinutes; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToMinutes; - } else if (aTimeoutUnit == TimeUnitEnum.HOURS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToHours; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToHours; - } else if (aTimeoutUnit == TimeUnitEnum.DAYS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToDays; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToDays; - } - - if (inBetweenDelays(aTimeoutFromUnifiedToSeconds, aTimeoutToUnifiedToSeconds, runtimeSeconds, GRACE_PERCENTAGE)) { - done(); - } else { - try { - should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not \"in between enough\" enough to expected values of: " + aTimeoutFromUnifiedToSeconds + " and " + aTimeoutToUnifiedToSeconds); - } catch (err) { - done(err); - } - } - } catch(err) { - done(err); - } - }); - var startTime = process.hrtime(); - delayNode1.receive({payload:"delayMe", delay:delay}); - }); - } - - it('variable delay set by msg.delay the message in milliseconds', function(done) { - variableDelayTest("200", "300", "milliseconds", 250, done); - }); - - it('variable delay is the default if msg.delay not specified', function(done) { - variableDelayTest("450", "550", "milliseconds", null, done); - }); - - it('variable delay is zero if msg.delay is zero', function(done) { - variableDelayTest("0", "20", "milliseconds", 0, done); - }); - - it('variable delay is zero if msg.delay is negative', function(done) { - variableDelayTest("0", "20", "milliseconds", -250, done); - }); - - /** - * Runs a RANDOM DELAY test, checks if the delay is in between the given timeout values - * @param aTimeoutFrom - the timeout quantity which is the minimal acceptable wait period - * @param aTimeoutTo - the timeout quantity which is the maximum acceptable wait period - * @param aTimeoutUnit - the unit of the timeout: milliseconds, seconds, minutes, hours, days - */ - function randomDelayTest(aTimeoutFrom, aTimeoutTo, aTimeoutUnit, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"random","timeout":5,"timeoutUnits":"seconds","rate":"1","rateUnits":"second","randomFirst":aTimeoutFrom,"randomLast":aTimeoutTo,"randomUnits":aTimeoutUnit,"drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - helperNode1.on("input", function(msg) { - try { - var endTime = process.hrtime(startTime); - var runtimeNanos = ( (endTime[0] * nanosToSeconds) + endTime[1] ); - var runtimeSeconds = runtimeNanos / nanosToSeconds; - var aTimeoutFromUnifiedToSeconds; - var aTimeoutToUnifiedToSeconds; - - // calculating the timeout in seconds - if (aTimeoutUnit == TimeUnitEnum.MILLIS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom / millisToSeconds; - aTimeoutToUnifiedToSeconds = aTimeoutTo / millisToSeconds; - } else if (aTimeoutUnit == TimeUnitEnum.SECONDS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom; - aTimeoutToUnifiedToSeconds = aTimeoutTo; - } else if (aTimeoutUnit == TimeUnitEnum.MINUTES) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToMinutes; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToMinutes; - } else if (aTimeoutUnit == TimeUnitEnum.HOURS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToHours; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToHours; - } else if (aTimeoutUnit == TimeUnitEnum.DAYS) { - aTimeoutFromUnifiedToSeconds = aTimeoutFrom * secondsToDays; - aTimeoutToUnifiedToSeconds = aTimeoutTo * secondsToDays; - } - - if (inBetweenDelays(aTimeoutFromUnifiedToSeconds, aTimeoutToUnifiedToSeconds, runtimeSeconds, GRACE_PERCENTAGE)) { - done(); - } else { - try { - should.fail(null, null, "Delayed runtime seconds " + runtimeSeconds + " was not \"in between enough\" enough to expected values of: " + aTimeoutFromUnifiedToSeconds + " and " + aTimeoutToUnifiedToSeconds); - } catch (err) { - done(err); - } - } - } catch(err) { - done(err); - } - }); - var startTime = process.hrtime(); - delayNode1.receive({payload:"delayMe"}); - }); - } - - it('randomly delays the message in seconds', function(done) { - randomDelayTest(0.4, 0.8, "seconds", done); - }); - - it('randomly delays the message in milliseconds', function(done) { - randomDelayTest("400", "800", "milliseconds", done); - }); - - it('randomly delays the message in minutes', function(done) { - randomDelayTest(0.0066, 0.0133, "minutes", done); - }); - - it('delays the message in hours', function(done) { - randomDelayTest(0.000111111, 0.000222222, "hours", done); - }); - - it('delays the message in days', function(done) { - randomDelayTest(0.0000046296, 0.0000092593, "days", done); - }); - - it('handles delay queue', function(done) { - this.timeout(2000); - var flow = [{id:"delayNode1", type :"delay","name":"delayNode","nbRateUnits":"1","pauseType":"queue","timeout":1,"timeoutUnits":"seconds","rate":4,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "_none_") { - msg.payload.should.equal(2); - (Date.now() - t).should.be.approximately(500,200); - } - else if (msg.topic === "A") { - msg.payload.should.equal(4); - (Date.now() - t).should.be.approximately(750,200); - } - else { - msg.topic.should.equal("B"); - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(1000,200); - done(); - } - } catch(e) { - done(e); - } - }); - setTimeout(function() { - // send test messages - delayNode1.receive({payload:1}); // send something with blank topic - delayNode1.receive({payload:1,topic:"A"}); // and something with a fixed topic - delayNode1.receive({payload:1,topic:"B"}); // and something else with a fixed topic (3rd tick) - delayNode1.receive({payload:2,topic:"A"}); // these should replace them in queue - delayNode1.receive({payload:3,topic:"A"}); // ditto - delayNode1.receive({payload:2}); // so only this should get out on first tick - delayNode1.receive({payload:4,topic:"A"}); // and this one on second tick - }, 275); // wait one tick beofre starting.. (to test no messages in queue path.) - }); - }); - - it('handles timed queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"timed","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "_none_") { - msg.payload.should.equal(2); - (Date.now() - t).should.be.approximately(500,200); - } - else if (msg.topic === "A") { - msg.payload.should.equal(4); - (Date.now() - t).should.be.approximately(500,200); - } - else { - msg.topic.should.equal("B"); - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(500,200); - done(); - } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1}); // send something with blank topic - delayNode1.receive({payload:1,topic:"A"}); // and something with a fixed topic - delayNode1.receive({payload:1,topic:"B"}); // and something else with a fixed topic - delayNode1.receive({payload:2,topic:"A"}); // these should replace them in queue - delayNode1.receive({payload:3,topic:"A"}); // ditto - delayNode1.receive({payload:2}); // so all should go on first tick - delayNode1.receive({payload:4,topic:"A"}); // and nothing on second - }); - }); - - it('can flush delay queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else { - if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({flush:true}); }); // reset the queue - }); - }); - - it('can part flush delay queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(200,100); - c = c + 1; - } - else if (msg.topic === "boo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(400,100); - c = c + 1; - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); - setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({flush:2}); }); - setTimeout( function() { delayNode1.receive({flush:1}); }, 200); - setTimeout( function() { delayNode1.receive({flush:4}); }, 400); - }); - }); - - it('can reset delay queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - c = c + 1; - }); - - setTimeout( function() { - if (c === 0) { done(); } - }, 700); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({reset:true}); }); // reset the queue - }); - }); - - it('can flush rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else { - if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({flush:true}); }); // reset the queue - }); - }); - - it('can part flush rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - else if (msg.topic === "bar") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(200,100); - c = c + 1; - } - else if (msg.topic === "boo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(400,100); - c = c + 1; - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); - setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"foo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({flush:2}); }); - setTimeout( function() { delayNode1.receive({flush:1}); }, 200); - setTimeout( function() { delayNode1.receive({flush:4}); }, 400); - }); - }); - - it('can part push to front of rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":1,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "aoo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(2,50); - c = c + 1; - } - else if (msg.topic === "eoo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(4,50); - c = c + 1; - } - else if (msg.topic === "coo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(202,50); - c = c + 1; - } - else if (msg.topic === "boo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(406,50); - c = c + 1; - } - else if (msg.topic === "doo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(4,50); - c = c + 1; - } - if (c === 5) { done(); } - } catch(e) { - done(e); - } - }); - - // send test messages - delayNode1.receive({payload:1,topic:"aoo"}); - setImmediate( function() { delayNode1.receive({payload:1,topic:"boo"}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"coo",toFront:true}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"doo",toFront:true,flush:1}); } ); - setImmediate( function() { delayNode1.receive({payload:1,topic:"eoo",toFront:true}); } ); - setImmediate( function() { delayNode1.receive({flush:1}); }); - setTimeout( function() { delayNode1.receive({flush:1}); }, 200); - setTimeout( function() { delayNode1.receive({flush:4}); }, 400); - }); - }); - - it('can reset rate limit queue', function(done) { - this.timeout(2000); - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, - {id:"helperNode1", type:"helper", wires:[]}]; - helper.load(delayNode, flow, function() { - var delayNode1 = helper.getNode("delayNode1"); - var helperNode1 = helper.getNode("helperNode1"); - var t = Date.now(); - var c = 0; - helperNode1.on("input", function(msg) { - msg.should.have.a.property('payload'); - msg.should.have.a.property('topic'); - try { - if (msg.topic === "foo") { - msg.payload.should.equal(1); - (Date.now() - t).should.be.approximately(0,100); - c = c + 1; - } - } catch(e) { - done(e); - } - }); - - setTimeout( function() { - if (c === 1) { done(); } - }, 700); - - // send test messages - delayNode1.receive({payload:1,topic:"foo"}); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({payload:1,topic:"bar"}); } ); // send something with blank topic - setImmediate( function() { delayNode1.receive({reset:true}); }); // reset the queue - }); - }); - - /* Messaging API support */ - function mapiDoneTestHelper(done, pauseType, drop, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const flow = [{id:"delayNode1",type:"delay",name:"delayNode", pauseType:pauseType, timeout:"1", timeoutUnits: "seconds", - rate: "1", nbRateUnits: "1", rateUnits: "second", randomFirst:"950", randomLast:"1050",randomUnits:"milliseconds", - drop: drop, wires: [[]]}, - {id:"completeNode1",type:"complete",scope: ["delayNode1"],uncaught:false,wires:[["helperNode1"]]}, - {id:"helperNode1",type:"helper", wires:[[]]}]; - const numMsgs = msgAndTimings.length; - helper.load([delayNode, completeNode], flow, function () { - const delayNode1 = helper.getNode("delayNode1"); - const helperNode1 = helper.getNode("helperNode1"); - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload', msgAndTimings[c].msg.payload); - (Date.now() - t).should.be.approximately(msgAndTimings[c].avr, msgAndTimings[c].var); - c += 1; - if ( c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setImmediate( function() { delayNode1.receive(msgAndTimings[i].msg); } ); - } - }); - } - - it('calls done when queued message is emitted (type: delay)', function(done) { - mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:1000, var:100}]); - }); - it('calls done when queued message is emitted (type: delayv)', function(done) { - mapiDoneTestHelper(done, "delayv", false, [{msg:{payload:1, delay:1000}, avr:1000, var:100}]); - }); - it('calls done when queued message is emitted (type: delay)', function(done) { - mapiDoneTestHelper(done, "random", false, [{msg:{payload:1}, avr:1000, var:100}]); - }); - it('calls done when queued message is cleared (type: delay)', function(done) { - mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,reset:true}, avr:100, var:100}]); - }); - it('calls done when queued message is cleared (type: delayv)', function(done) { - mapiDoneTestHelper(done, "delayv", false, [{msg:{payload:1, delay:1000}, avr:100, var:100}, - {msg:{payload:2, reset:true}, avr:100, var:100}]); - }); - it('calls done when queued message is cleared (type: random)', function(done) { - mapiDoneTestHelper(done, "random", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,reset:true}, avr:100, var:100}]); - }); - it('calls done when queued message is flushed (type: delay)', function(done) { - mapiDoneTestHelper(done, "delay", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,flush:true}, avr:100, var:100}]); - }); - it('calls done when queued message is flushed (type: delayv)', function(done) { - mapiDoneTestHelper(done, "delayv", false, [{msg:{payload:1, delay:1000}, avr:100, var:100}, - {msg:{payload:2, flush:true}, avr:100, var:100}]); - }); - it('calls done when queued message is flushed (type: random)', function(done) { - mapiDoneTestHelper(done, "random", false, [{msg:{payload:1}, avr:100, var:100}, - {msg:{payload:2,flush:true}, avr:100, var:100}]); - }); - it('calls done when rated message is emitted (drop: false)', function(done) { - mapiDoneTestHelper(done, "rate", false, [{msg:{payload:1}, avr:0, var:100}, - {msg:{payload:2}, avr:1000, var:100}]); - }); - it('calls done when rated message is emitted (drop: true)', function(done) { - mapiDoneTestHelper(done, "rate", true, [{msg:{payload:1}, avr:0, var:100}, - {msg:{payload:2}, avr:0, var:100}]); - }); - it('calls done when rated message is flushed', function(done) { - mapiDoneTestHelper(done, "rate", false, [{msg:{payload:1}, avr:0, var:100}, - {msg:{payload:2}, avr:0, var:100}, - {msg:{payload:3,flush:true}, avr:0, var:100}]); - }); - it('calls done when queued messages are sent (queue)', function(done) { - this.timeout(3000); - mapiDoneTestHelper(done, "queue", false, [{msg:{payload:1,topic:"A"}, avr:1000, var:700}, - {msg:{payload:2,topic:"B"}, avr:2000, var:700}]); - }); - it('calls done when queued messages are sent (timed)', function(done) { - mapiDoneTestHelper(done, "timed", false, [{msg:{payload:1,topic:"a"}, avr:500, var:700}, - {msg:{payload:2,topic:"b"}, avr:500, var:700}]); - }); - it('calls done when queue is reset (queue/timed)', function(done) { - mapiDoneTestHelper(done, "timed", false, [{msg:{payload:1,topic:"a"}, avr:0, var:500}, - {msg:{payload:2,reset:true}, avr:0, var:500}]); - }); - it('calls done when queue is flushed (queue/timed)', function(done) { - mapiDoneTestHelper(done, "timed", false, [{msg:{payload:1,topic:"a"}, avr:0, var:500}, - {msg:{payload:2,flush:true}, avr:0, var:500}]); - }); -}); diff --git a/test/nodes/core/function/89-trigger_spec.js b/test/nodes/core/function/89-trigger_spec.js deleted file mode 100644 index 33401e540..000000000 --- a/test/nodes/core/function/89-trigger_spec.js +++ /dev/null @@ -1,1198 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var helper = require("node-red-node-test-helper"); -var triggerNode = require("nr-test-utils").require("@node-red/nodes/core/function/89-trigger.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); -var RED = require("nr-test-utils").require("node-red/lib/red"); - -describe('trigger node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory0: { - module: "memory" - }, - memory1: { - module: "memory" - }, - memory2: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function () { - return Context.clean({allNodes: {}}); - }).then(function () { - return Context.close(); - }).then(function () { - helper.stopServer(done); - }); - }); - - it("should be loaded with correct defaults", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'triggerNode'); - n1.should.have.property('op1', '1'); - n1.should.have.property('op2', '0'); - n1.should.have.property('op1type', 'str'); - n1.should.have.property('op2type', 'str'); - n1.should.have.property('extend', "false"); - n1.should.have.property('units', 'ms'); - n1.should.have.property('duration', 250); - done(); - }); - }); - - it("should be able to set delay in seconds", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"s", duration:"1", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('duration', 1000); - done(); - }); - }); - - it("should be able to set delay in minutes", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"min", duration:"1", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('duration', 60000); - done(); - }); - }); - - it("should be able to set delay in hours", function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", units:"hr", duration:"1", "wires":[[]]}]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('duration', 3600000); - done(); - }); - }); - - function basicTest(type, val, rval) { - it('should output 1st value when triggered ('+type+')', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:val, op1type:type, op2:"", op2type:"null", duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - process.env[val] = rval; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - if (rval) { - msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); - } - else { - msg.should.have.property("payload", val); - } - delete process.env[val]; - done(); - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should output 2st value when triggered ('+type+')', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"str", op2:val, op2type:type, duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - process.env[val] = rval; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.property("payload", "foo"); - c++; - } - else { - if (rval) { - msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); - } - else { - msg.should.have.property("payload", val); - } - delete process.env[val]; - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - } - - basicTest("num", 10); - basicTest("str", "10"); - basicTest("bool", true); - var val_json = '{ "x":"vx", "y":"vy", "z":"vz" }'; - basicTest("json", val_json, JSON.parse(val_json)); - var val_buf = "[1,2,3,4,5]"; - basicTest("bin", val_buf, Buffer.from(JSON.parse(val_buf))); - basicTest("env", "NR-TEST", "env-val"); - - it('should output 1 then 0 when triggered (default)', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", '1'); - c+=1; - } - else { - msg.should.have.a.property("payload", '0'); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should ignore any other inputs while triggered if extend is false', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"50",wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var errored = false; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", '1'); - } - else { - msg.should.have.a.property("payload", '0'); - } - c+=1; - }catch(err) { - errored = true; - done(err); - } - }); - setTimeout( function() { - if (!errored) { - try { - c.should.equal(2); - done(); - } catch(err) { - done(err); - } - } - },100); - n1.emit("input", {payload:null}); - setTimeout( function() { - n1.emit("input", {payload:null}); - },10); - setTimeout( function() { - n1.emit("input", {payload:null}); - },30); - }); - }); - - it('should ignore msg.delay if overrideDelay not set', function(done) { - var flow = [ - {"id":"n1", "type":"trigger", "name":"triggerNode", duration:"50",wires:[["n2"]] }, - {id:"n2", type:"helper"} - ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var firstTime; - n2.on("input", function(msg) { - if (c === 0) { - firstTime = Date.now(); - } else if (c === 1) { - try { - var delta = Date.now() - firstTime; - delta.should.be.greaterThan(30); - delta.should.be.lessThan(100); - done(); - } catch(err) { - done(err); - } - } - c++; - }); - n1.emit("input", {payload:null, delay: 300}); - }); - }); - - it('should use msg.delay if overrideDelay is set', function(done) { - var flow = [ - {"id":"n1", "type":"trigger", "name":"triggerNode", overrideDelay: true, duration:"50",wires:[["n2"]] }, - {id:"n2", type:"helper"} - ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var firstTime; - n2.on("input", function(msg) { - if (c === 0) { - firstTime = Date.now(); - } else if (c === 1) { - try { - var delta = Date.now() - firstTime; - delta.should.be.greaterThan(270); - delta.should.be.lessThan(380); - done(); - } catch(err) { - done(err); - } - } - c++; - }); - n1.emit("input", {payload:null, delay: 300}); - }); - }); - - - it('should handle true and false as strings and delay of 0', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"true",op1type:"val",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", true); - c+=1; - } - else { - msg.should.have.a.property("payload", false); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should handle multiple topics as one if not asked to handle', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"all", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "A"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,topic:"A"}); - n1.emit("input", {payload:2,topic:"B"}); - n1.emit("input", {payload:3,topic:"C"}); - }); - }); - - it('should handle multiple topics individually if asked to do so', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "B"); - } - else if (c === 3) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "C"); - } - else if (c === 4) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "A"); - } - else if (c === 5) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "B"); - } - else if (c === 6) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "C"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,topic:"A"}); - n1.emit("input", {payload:2,topic:"B"}); - n1.emit("input", {payload:3,topic:"C"}); - }); - }); - - it('should handle multiple topics individually, and extend one, if asked to do so', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", extend:"true", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "B"); - } - else if (c === 3) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("topic", "C"); - } - else if (c === 4) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "A"); - } - else if (c === 5) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "C"); - } - else if (c === 6) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("topic", "B"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,topic:"A"}); - n1.emit("input", {payload:2,topic:"B"}); - n1.emit("input", {payload:3,topic:"C"}); - setTimeout( function() { n1.emit("input", {payload:2,topic:"B"})}, 20 ); - }); - }); - - it('should handle multiple other properties individually if asked to do so', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", bytopic:"topic", topic:"foo", op1:"1", op2:"0", op1type:"num", op2type:"num", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - if (c === 1) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("foo", "A"); - } - else if (c === 2) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("foo", "B"); - } - else if (c === 3) { - msg.should.have.a.property("payload", 1); - msg.should.have.a.property("foo", "C"); - } - else if (c === 4) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("foo", "A"); - } - else if (c === 5) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("foo", "B"); - } - else if (c === 6) { - msg.should.have.a.property("payload", 0); - msg.should.have.a.property("foo", "C"); - done(); - } - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:1,foo:"A"}); - n1.emit("input", {payload:2,foo:"B"}); - n1.emit("input", {payload:3,foo:"C"}); - }); - }); - - it('should be able to return things from flow and global context variables', function(done) { - var spy = sinon.stub(RED.util, 'evaluateNodeProperty').callsFake( - function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } } - ); - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"flow", op2:"bar", op2type:"global", duration:"20", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "foo"); - c+=1; - } - else { - msg.should.have.a.property("payload", "bar"); - RED.util.evaluateNodeProperty.restore(); - done(); - } - } - catch(err) { RED.util.evaluateNodeProperty.restore(); done(err); } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should be able to return things from persistable flow and global context variables', function (done) { - var flow = [{"id": "n1", "type": "trigger", "name": "triggerNode", "op1": "#:(memory1)::foo", "op1type": "flow", - "op2": "#:(memory1)::bar", "op2type": "global", "duration": "20", "wires": [["n2"]], "z": "flow" }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function (msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "foo"); - c += 1; - } else { - msg.should.have.a.property("payload", "bar"); - done(); - } - } catch (err) { - done(err); - } - }); - var context = n1.context(); - var flow = context.flow; - var global = context.global; - flow.set("foo", "foo", "memory1", function (err) { - global.set("bar", "bar", "memory1", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to return things from multiple persistable global context variables', function (done) { - var flow = [{"id": "n1", "z": "flow", "type": "trigger", - "duration": "20", "wires": [["n2"]], - "op1": "#:(memory1)::val", "op1type": "global", - "op2": "#:(memory2)::val", "op2type": "global" - }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - if (count === 0) { - msg.should.have.a.property("payload", "foo"); - } - else { - msg.should.have.a.property("payload", "bar"); - } - count++; - if (count === 1) { - done(); - } - } - catch (err) { - done(err); - } - }); - var global = n1.context().global; - global.set("val", "foo", "memory1", function (err) { - global.set("val", "bar", "memory2", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to return things from multiple persistable flow context variables', function (done) { - var flow = [{"id": "n1", "z": "flow", "type": "trigger", - "duration": "20", "wires": [["n2"]], - "op1": "#:(memory1)::val", "op1type": "flow", - "op2": "#:(memory2)::val", "op2type": "flow" - }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - if (count === 0) { - msg.should.have.a.property("payload", "foo"); - } - else { - msg.should.have.a.property("payload", "bar"); - } - count++; - if (count === 1) { - done(); - } - } - catch (err) { - done(err); - } - }); - var flow = n1.context().flow; - flow.set("val", "foo", "memory1", function (err) { - flow.set("val", "bar", "memory2", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to return things from multiple persistable flow & global context variables', function (done) { - var flow = [{"id": "n1", "z": "flow", "type": "trigger", - "duration": "20", "wires": [["n2"]], - "op1": "#:(memory1)::val", "op1type": "flow", - "op2": "#:(memory2)::val", "op2type": "global" - }, - {"id": "n2", "type": "helper"}]; - helper.load(triggerNode, flow, function () { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function (msg) { - try { - if (count === 0) { - msg.should.have.a.property("payload", "foo"); - } - else { - msg.should.have.a.property("payload", "bar"); - } - count++; - if (count === 1) { - done(); - } - } - catch (err) { - done(err); - } - }); - var context = n1.context(); - var flow = context.flow; - var global = context.flow; - flow.set("val", "foo", "memory1", function (err) { - global.set("val", "bar", "memory2", function (err) { - n1.emit("input", { payload: null }); - }); - }); - }); - }); - }); - - it('should be able to not output anything on first trigger', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"nul", op1:"true",op2:"false",op2type:"val",duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.a.property("payload", false); - done(); - } catch(err) { - done(err); - } - }); - n1.emit("input", {payload:null}); - }); - }); - - it('should be able to not output anything on second edge', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op2type:"nul", op1:"true",op1type:"val", op2:"false", duration:"30", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.a.property("payload", true); - c += 1; - } - catch(err) { done(err); } - }); - setTimeout( function() { - c.should.equal(1); // should only have had one output. - done(); - },90); - n1.emit("input", {payload:null}); - }); - }); - - it('should be able to reset correctly having not output anything on second edge', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op2type:"nul", op1:"true",op1type:"val", op2:"false", duration:"100", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - var errors = []; - n2.on("input", function(msg) { - try { - msg.should.have.a.property("topic", "pass") - msg.should.have.a.property("payload", true); - c += 1; - } - catch(err) { errors.push(err) } - }); - setTimeout( function() { - if (errors.length > 0) { - done(errors[0]) - } else { - c.should.equal(2); - done(); - } - },350); - n1.emit("input", {payload:1, topic:"pass"}); - setTimeout( function() { - n1.emit("input", {payload:2, topic:"should-block"}); - },50); - setTimeout( function() { - n1.emit("input", {payload:3, topic:"pass"}); - },200); - setTimeout( function() { - n1.emit("input", {payload:2, topic:"should-block"}); - },250); - }); - }); - - it('should be able to extend the delay', function(done) { - this.timeout(5000); // add extra time for flake - var spy = sinon.stub(RED.util, 'evaluateNodeProperty').callsFake( - function(arg1, arg2, arg3, arg4, arg5) { if (arg5) { arg5(null, arg1) } else { return arg1; } } - ); - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"flow", op1:"foo", op2:"bar", op2type:"global", duration:"100", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "foo"); - c += 1; - } - else { - msg.should.have.a.property("payload", "bar"); - //console.log(Date.now() - ss); - (Date.now() - ss).should.be.greaterThan(149); - spy.restore(); - done(); - } - } - catch(err) { spy.restore(); done(err); } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello"}); - setTimeout( function() { - n1.emit("input", {payload:null}); - },50); - }); - }); - - it('should be able to extend the delay (but with no 2nd output)', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op2type:"nul", op1:"false", op2:"true", duration:"200", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Hello"); - c += 1; - } - else { - msg.should.have.a.property("payload", "World"); - (Date.now() - ss).should.be.greaterThan(300); - done(); - } - } catch(err) { - console.log(err); - done(err); - } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello"}); - setTimeout( function() { - n1.emit("input", {payload:"Error"}); - },50); - setTimeout( function() { - n1.emit("input", {payload:"Error"}); - },100); - setTimeout( function() { - n1.emit("input", {payload:"World"}); - },330); - }); - }); - - it('should be able to extend the delay and output the most recent payload', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"60", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.a.property("payload", "World"); - (Date.now() - ss).should.be.greaterThan(120); - done(); - } - catch(err) { done(err); } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello"}); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye"}); - },40); - setTimeout( function() { - n1.emit("input", {payload:"World"}); - },80); - }); - }); - - it('should be able output the 2nd payload', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"false", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"50", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Goodbye"); - msg.should.have.a.property("topic", "test2"); - c += 1; - } - else { - msg.should.have.a.property("payload", "World"); - msg.should.have.a.property("topic", "test3"); - (Date.now() - ss).should.be.greaterThan(70); - done(); - } - } - catch(err) { done(err); } - }); - var ss = Date.now(); - n1.emit("input", {payload:"Hello", topic:"test1"}); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye", topic:"test2"}); - },20); - setTimeout( function() { - n1.emit("input", {payload:"World", topic:"test3"}); - },80); - }); - }); - - it('should be able output the 2nd payload and handle multiple topics', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"false", op1type:"nul", op2type:"payl", op1:"false", op2:"true", duration:"80", bytopic:"topic", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Goodbye1"); - msg.should.have.a.property("topic", "test1"); - c += 1; - } - else { - msg.should.have.a.property("payload", "Goodbye2"); - msg.should.have.a.property("topic", "test2"); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"Hello1", topic:"test1"}); - setTimeout( function() { - n1.emit("input", {payload:"Hello2", topic:"test2"}); - },20); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye2", topic:"test2"}); - },20); - setTimeout( function() { - n1.emit("input", {payload:"Goodbye1", topic:"test1"}); - },20); - }); - }); - - it('should be able to apply mustache templates to payloads', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"{{payload}}", op2:"{{topic}}", duration:"50", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "Hello"); - c+=1; - } - else { - msg.should.have.a.property("payload", "World"); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"Hello",topic:"World"}); - }); - }); - - it('should be able to send 2nd message to a 2nd output', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"hello", op2:"world", duration:"50", outputs:2, wires:[["n2"],["n3"]] }, - {id:"n2", type:"helper"}, {id:"n3", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", "hello"); - msg.should.have.a.property("topic", "test"); - c+=1; - } - else { done(err); } - } - catch(err) { done(err); } - }); - n3.on("input", function(msg) { - try { - if (c === 1) { - msg.should.have.a.property("payload", "world"); - msg.should.have.a.property("topic", "test"); - done(); - } - else { done(err); } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"go",topic:"test"}); - }); - }); - - it('should handle string null as null', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"pay", op1:"null", op2:"null", duration:"40", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", null); - c+=1; - } - else { - msg.should.have.a.property("payload", "World"); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"World"}); - }); - }); - - it('should handle string null as null on op2', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1type:"val", op2type:"val", op1:"null", op2:"null", duration:"40", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - if (c === 0) { - msg.should.have.a.property("payload", null); - c+=1; - } - else { - msg.should.have.a.property("payload", null); - done(); - } - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"null"}); - }); - }); - - it('should be able to set infinite timeout, and clear timeout', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"0", extend: false, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },20); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {reset:true}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - }); - }); - - it('should be able to set infinite timeout, and clear timeout by message', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:"foo"}); // don't clear the blockage - n1.emit("input", {payload:"boo"}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },50); - }); - }); - - it('should be able to set infinite timeout, and clear timeout by boolean true', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"true", duration:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },20); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:false}); // don't clear the blockage - n1.emit("input", {payload:true}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - }); - }); - - it('should be able to set infinite timeout, and clear timeout by boolean false', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"false", duration:"0", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - c += 1; - msg.should.have.a.property("payload", "1"); - } - catch(err) { done(err); } - }); - setTimeout( function() { - if (c === 2) { done(); } - else { - done(new Error("Too many messages received")); - } - },20); - n1.emit("input", {payload:null}); // trigger - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:null}); // blocked - n1.emit("input", {payload:"foo"}); // don't clear the blockage - n1.emit("input", {payload:false}); // clear the blockage - n1.emit("input", {payload:null}); // trigger - }); - }); - - it('should be able to set a repeat, and clear loop by reset', function(done) { - var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", op1:"", op1type:"pay", duration:-25, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(triggerNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - c += 1; - try { - msg.should.have.property('payload','foo'); - msg.payload = "bar"; // try to provoke pass by reference error - } - catch(err) { done(err); } - }); - n1.emit("input", {payload:"foo"}); // trigger - n1.emit("input", {payload:"foo"}); // trigger - setTimeout( function() { - n1.emit("input", {reset:true}); // reset - },90); - setTimeout( function() { - c.should.within(2,5); // should send foo between 2 and 5 times. - done(); - },180); - }); - }); - describe('messaging API', function () { - function mapiDoneTriggerTestHelper(done, nodeSetting, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - { ...nodeSetting, id: "triggerNode1", type: "trigger", wires: [[]] }, - { id: "completeNode1", type: "complete", scope: ["triggerNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["triggerNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([triggerNode, completeNode, catchNode], flow, function () { - const triggerNode1 = helper.getNode("triggerNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 3; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { triggerNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when first message has been processed', function (done) { - // not when second and more messages are emitted. - mapiDoneTriggerTestHelper(done, { units:"s", duration:"1" }, [ - { msg: { seq: 0, payload: "A"}, delay: 0, avr: 0, var: 100} - ]); - }); - it('should call done() when it receives reset message', function (done) { - mapiDoneTriggerTestHelper(done, {units:"s", duration:"1"}, [ - {msg: { seq: 0, payload: "A", reset:true}, delay: 0, avr: 0, var:100} - ]); - }) - }); -}); diff --git a/test/nodes/core/function/90-exec_spec.js b/test/nodes/core/function/90-exec_spec.js deleted file mode 100644 index c397ea27d..000000000 --- a/test/nodes/core/function/90-exec_spec.js +++ /dev/null @@ -1,973 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var helper = require("node-red-node-test-helper"); -var execNode = require("nr-test-utils").require("@node-red/nodes/core/function/90-exec.js"); -var osType = require("os").type(); - -var child_process = require('child_process'); - -describe('exec node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - afterEach(function(done) { - helper.unload().then(function() { - helper.stopServer(done); - }); - }); - - it('should be loaded with any defaults', function(done) { - var flow = [{id:"n1", type:"exec", name: "exec1"}]; - helper.load(execNode, flow, function() { - try { - var n1 = helper.getNode("n1"); - n1.should.have.property("name", "exec1"); - n1.should.have.property("cmd", ""); - n1.should.have.property("append", ""); - n1.should.have.property("addpay","payload"); - n1.should.have.property("timer",0); - n1.should.have.property("oldrc","false"); - n1.should.have.property("execOpt"); - n1.execOpt.should.have.property("encoding", 'binary'); - n1.execOpt.should.have.property("maxBuffer", 10000000); - n1.execOpt.should.have.property("windowsHide", false); - n1.should.have.property("spawnOpt"); - n1.spawnOpt.should.have.property("windowsHide", false); - done(); - } catch(err) { - done(err); - } - }); - }); - - describe('calling exec', function() { - - it('should exec a simple command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - // arg3(error,stdout,stderr); - arg3(null,arg1,arg1.toUpperCase()); - }); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received = received + 1; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("echo"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ECHO"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",0); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and"}); - }); - }); - - it('should exec a simple command with appended value from message', function (done) { - var flow = [{id:"n1", type:"exec", wires:[["n2"]], command:"echo", addpay:"topic", append:"more", oldrc:"false"}, - {id:"n2", type:"helper"}]; - helper.load(execNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("bar more\n"); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:"foo", topic:"bar"}); - }); - }); - - it('should exec a simple command with extra parameters', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:"payload", append:"more", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - //console.log(arg1); - // arg3(error,stdout,stderr); - arg3(null,arg1,arg1.toUpperCase()); - }); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - received++; - if (received < 2) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("echo and more"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ECHO AND MORE"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:"and"}); - }); - }); - - it('should be able to return a binary buffer', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:"more", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - //console.log(arg1); - // arg3(error,stdout,stderr); - arg3("error",Buffer.from([0x01,0x02,0x03,0x88]),Buffer.from([0x01,0x02,0x03,0x88])); - }); - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - //console.log("n2",msg); - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.length.should.equal(4); - child_process.exec.restore(); - done(); - } catch(err) { - child_process.exec.restore(); - done(err); - } - }); - n1.receive({}); - }); - }); - - it('should be able to timeout a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - // Although Windows timeout command is equivalent to sleep, this cannot be used because it promptly outputs a message. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"0.3", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"0.3", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - try { - msg.should.have.property("rc"); - msg.rc.should.have.property("code",null); - msg.rc.should.have.property("signal","SIGTERM"); - } catch(err) { done(err); } - }); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:""}); - },150); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command - SIGINT', function(done) { - var flow; - var sig = "SIGINT"; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - try { - msg.should.have.property("rc"); - msg.rc.should.have.property("code",null); - msg.rc.should.have.property("signal","SIGINT"); - } catch(err) { done(err); } - }); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal",sig); - done(); - } catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:"SIGINT"}); - },150); - n1.receive({}); - }); - }); - - it('should return the rc for a failing command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"error", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - //console.log(arg1); - // arg3(error,stdout,stderr); - arg3({code: 1},arg1,arg1.toUpperCase()); - }); - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received++; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("error"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",1); - msg.rc.should.have.property("message",undefined); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ERROR"); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and"}); - }); - }); - - it('should preserve existing properties on msg object', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - // arg3(error,stdout,stderr); - arg3(null,arg1,arg1.toUpperCase()); - }); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received = received + 1; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("echo"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - msg.should.have.property("foo","bar"); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ECHO"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",0); - msg.should.have.property("foo","bar"); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",0); - msg.should.have.property("foo","bar"); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and", foo:"bar"}); - }); - }); - - it('should preserve existing properties on msg object for a failing command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"error", addpay:false, append:"", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - var spy = sinon.stub(child_process, 'exec').callsFake( - function(arg1, arg2, arg3, arg4) { - // arg3(error,stdout,stderr); - arg3({code: 1},arg1,arg1.toUpperCase()); - }); - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null,null]; - var completeTest = function() { - received++; - if (received < 3) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("error"); - msg.should.have.property("rc"); - msg.rc.should.have.property("code",1); - msg.rc.should.have.property("message",undefined); - msg.should.have.property("foo",null); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal("ERROR"); - msg.should.have.property("foo",null); - - msg = messages[2]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - msg.should.have.property("foo",null); - - child_process.exec.restore(); - done(); - } - catch(err) { - child_process.exec.restore(); - done(err); - } - }; - n2.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n3.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[2] = msg; - completeTest(); - }); - n1.receive({payload:"and", foo:null}); - }); - }); - - }); - - describe('calling spawn', function() { - - it('should spawn a simple command', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - // Need to use cmd to spawn a process because Windows echo command is a built-in command and cannot be spawned. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "hello world\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "hello world\n"; - } - var events = require('events'); - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var payload = ""; - n2.on("input", function(msg) { - //console.log(msg); - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - payload += msg.payload; - if (payload.endsWith("\n")) { - payload.should.equal(expected); - done(); - } - } - catch(err) { done(err); } - }); - n1.receive({payload:"hello world"}); - }); - }); - - it('should spawn a simple command with a non string payload parameter', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo", addpay:true, append:" deg C", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "12345 deg C\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:" deg C", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "12345 deg C\n"; - } - var payload = ""; - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - //console.log(msg); - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - payload += msg.payload; - if (payload.endsWith("\n")) { - payload.should.equal(expected); - done(); - } - } - catch(err) { done(err); } - }); - n1.receive({payload:12345}); - }); - }); - - it('should spawn a simple command and return binary buffer', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo", addpay:true, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - if (osType === "Windows_NT") { - msg.payload.length.should.equalOneOf(6,8); - } else { - msg.payload.length.should.equal(7); - } - done(); - } - catch(err) { done(err); } - }); - n1.receive({payload:Buffer.from([0x01,0x02,0x03,0x88])}); - }); - }); - - it('should work if passed multiple words to spawn command', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\n"; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - received++; - if (received < 2) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal(expected); - - msg = messages[1]; - msg.should.have.property("payload"); - should.exist(msg.payload); - msg.payload.should.have.property("code",0); - done(); - } - catch(err) { - done(err); - } - }; - - n2.on("input", function(msg) { - var payload = msg.payload; - if (messages[0]) { - messages[0].payload += payload; - } - else { - messages[0] = msg; - } - if (payload.endsWith("\n")) { - completeTest(); - } - }); - n4.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:null,fred:123}); - }); - }); - - it('should return an error for a bad command', function(done) { - var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"madeupcommandshouldfail", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("code"); - msg.payload.code.should.be.below(0); - done(); - } - catch(err) { done(err); } - }); - n1.receive({payload:null}); - }); - }); - - it('should return an error for a failing command', function(done) { - var flow; - var expected; - var expectedFound = false; - if (osType === "Windows_NT") { - // Cannot use mkdir because Windows mkdir command automatically creates non-existent directories. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "IP address must be specified."; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"mkdir /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = ' directory'; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n3.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - if (msg.payload.indexOf(expected) >= 0) { - // The error text on the stderr stream might get sent in more than one piece. - // We only need to know that it occurred before the return code is sent, - // as checked below in node n4. - expectedFound = true; - } - } - catch(err) { done(err); } - }); - n4.on("input", function(msg) { - try { - expectedFound.should.be.true; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - done(); - } - catch(err) { done(err); } - }); - n1.receive({payload:null}); - }); - }); - - it('should be able to timeout a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000", timer:"0.3", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"0.3", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("code",null); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command', function(done) { - var flow; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal","SIGTERM"); - done(); - } - catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:""}); - },150); - n1.receive({}); - }); - }); - - it('should be able to kill a long running command - SIGINT', function(done) { - var flow; - var sig = "SIGINT"; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping", addpay:false, append:"192.0.2.0 -n 1 -w 1000 > NUL", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - n4.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("signal",sig); - done(); - } - catch(err) { done(err); } - }); - setTimeout(function() { - n1.receive({kill:"SIGINT"}); - },150); - n1.receive({}); - }); - }); - - it('should preserve existing properties on msg object', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"cmd /C echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\r\n"; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"echo this now works", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "this now works\n"; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - received++; - if (received < 2) { - return; - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.payload.should.equal(expected); - msg.should.have.property("foo",123); - - msg = messages[1]; - msg.should.have.property("payload"); - should.exist(msg.payload); - msg.payload.should.have.property("code",0); - msg.should.have.property("foo",123); - - done(); - } - catch(err) { - done(err); - } - }; - - n2.on("input", function(msg) { - var payload = msg.payload; - if (messages[0]) { - messages[0].payload += payload; - } - else { - messages[0] = msg; - } - if (payload.endsWith("\n")) { - completeTest(); - } - }); - n4.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:null,foo:123}); - }); - }); - - it('should preserve existing properties on msg object for a failing command', function(done) { - var flow; - var expected; - if (osType === "Windows_NT") { - // Cannot use mkdir because Windows mkdir command automatically creates non-existent directories. - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"ping /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = "IP address must be specified."; - } else { - flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"mkdir /foo/bar/doo/dah", addpay:false, append:"", useSpawn:"true", oldrc:"false"}, - {id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}]; - expected = ' directory'; - } - helper.load(execNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - var n4 = helper.getNode("n4"); - var received = 0; - var messages = [null,null]; - var completeTest = function() { - if (messages[0] === null || messages[1] === null) { - // We have not yet had responses on both ports. - return - } - try { - var msg = messages[0]; - msg.should.have.property("payload"); - msg.payload.should.be.a.String(); - msg.should.have.property("foo","baz"); - - msg = messages[1]; - msg.should.have.property("payload"); - msg.payload.should.have.property("code",1); - msg.should.have.property("foo","baz"); - - done(); - } - catch(err) { - done(err); - } - }; - - n3.on("input", function(msg) { - messages[0] = msg; - completeTest(); - }); - n4.on("input", function(msg) { - messages[1] = msg; - completeTest(); - }); - n1.receive({payload:null,foo:"baz"}); - }); - }); - - }); -}); diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js deleted file mode 100644 index 07bed5a01..000000000 --- a/test/nodes/core/network/21-httprequest_spec.js +++ /dev/null @@ -1,2221 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var http = require("http"); -var https = require("https"); -var should = require("should"); -var express = require("express"); -var bodyParser = require('body-parser'); -var stoppable = require('stoppable'); -var helper = require("node-red-node-test-helper"); -var httpRequestNode = require("nr-test-utils").require("@node-red/nodes/core/network/21-httprequest.js"); -var tlsNode = require("nr-test-utils").require("@node-red/nodes/core/network/05-tls.js"); -var httpProxyNode = require("nr-test-utils").require("@node-red/nodes/core/network/06-httpproxy.js"); -var hashSum = require("hash-sum"); -var httpProxy = require('proxy'); -var cookieParser = require('cookie-parser'); -var multer = require("multer"); -var RED = require("nr-test-utils").require("node-red/lib/red"); -var fs = require('fs-extra'); -var auth = require('basic-auth'); - -describe('HTTP Request Node', function() { - var testApp; - var testServer; - var testPort = 10234; - var testSslServer; - var testSslPort = 10334; - var testProxyServer; - var testProxyPort = 10444; - var testProxyServerAuth; - var testProxyAuthPort = 10554; - var testSslClientServer; - var testSslClientPort = 10664; - - //save environment variables - var preEnvHttpProxyLowerCase; - var preEnvHttpProxyUpperCase; - var preEnvNoProxyLowerCase; - var preEnvNoProxyUpperCase; - - //rediect cookie variables - var receivedCookies = {}; - - function startServer(done) { - testPort += 1; - testServer = stoppable(http.createServer(testApp)); - testServer.listen(testPort,function(err) { - testSslPort += 1; - console.log("ssl port", testSslPort); - var sslOptions = { - key: fs.readFileSync('test/resources/ssl/server.key'), - cert: fs.readFileSync('test/resources/ssl/server.crt') - /* - Country Name (2 letter code) [AU]: - State or Province Name (full name) [Some-State]: - Locality Name (eg, city) []: - Organization Name (eg, company) [Internet Widgits Pty Ltd]: - Organizational Unit Name (eg, section) []: - Common Name (e.g. server FQDN or YOUR name) []:localhost - Email Address []: - - Please enter the following 'extra' attributes to be sent with your certificate request - A challenge password []: - An optional company name []: - */ - }; - testSslServer = stoppable(https.createServer(sslOptions,testApp)); - testSslServer.listen(testSslPort, function(err){ - if (err) { - console.log(err); - } else { - console.log("started testSslServer"); - } - }); - - testSslClientPort += 1; - var sslClientOptions = { - key: fs.readFileSync('test/resources/ssl/server.key'), - cert: fs.readFileSync('test/resources/ssl/server.crt'), - ca: fs.readFileSync('test/resources/ssl/server.crt'), - requestCert: true - }; - testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp)); - testSslClientServer.listen(testSslClientPort, function(err){ - console.log("ssl-client", err) - }); - - testProxyPort += 1; - testProxyServer = stoppable(httpProxy(http.createServer())) - - testProxyServer.on('request', function(req,res){ - if (!res.headersSent) { - res.setHeader("x-testproxy-header", "foobar") - } - }) - testProxyServer.listen(testProxyPort) - - testProxyAuthPort += 1 - testProxyServerAuth = stoppable(httpProxy(http.createServer())) - testProxyServerAuth.authenticate = function(req,callback){ - var authHeader = req.headers['proxy-authorization']; - if (authHeader) { - var user = auth.parse(authHeader) - if (user.name == "foouser" && user.pass == "barpassword") { - callback(null, true) - } else { - callback(null, false) - } - } else { - callback(null, false) - } - } - testProxyServerAuth.on('request', function(req,res){ - if (!res.headersSent) { - res.setHeader("x-testproxy-header", "foobar") - } - }) - testProxyServerAuth.listen(testProxyAuthPort) - - done(err); - }); - } - - function getTestURL(url) { - return "http://localhost:"+testPort+url; - } - - function getSslTestURL(url) { - return "https://localhost:"+testSslPort+url; - } - - function getSslClientTestURL(url) { - return "https://localhost:"+testSslClientPort+url; - } - - function getDifferentTestURL(url) { - return "http://127.0.0.1:"+testPort+url; - } - - function getSslTestURLWithoutProtocol(url) { - return "localhost:"+testSslPort+url; - } - - function deleteProxySetting() { - delete process.env.http_proxy; - delete process.env.HTTP_PROXY; - delete process.env.no_proxy; - delete process.env.NO_PROXY; - } - - before(function(done) { - - testApp = express(); - - // The fileupload test needs a different set of middleware - so mount - // as a separate express instance - var fileUploadApp = express(); - var mp = multer({ storage: multer.memoryStorage() }).any(); - fileUploadApp.post("/file-upload",function(req,res,next) { - mp(req,res,function(err) { - req._body = true; - next(err); - }) - },bodyParser.json(),function(req,res) { - res.json({ - body: req.body, - files: req.files - }) - }); - testApp.use(fileUploadApp); - - testApp.use(bodyParser.raw({type:"*/*"})); - testApp.use(cookieParser(undefined,{decode:String})); - testApp.get('/statusCode204', function(req,res) { res.status(204).end();}); - testApp.get('/text', function(req, res){ res.send('hello'); }); - testApp.get('/redirectToText', function(req, res){ res.status(302).set('Location', getTestURL('/text')).end(); }); - testApp.get('/json-valid', function(req, res){ res.json({a:1}); }); - testApp.get('/json-invalid', function(req, res){ res.set('Content-Type', 'application/json').send("{a:1"); }); - testApp.get('/headersInspect', function(req, res){ res.set('x-test-header', 'bar').send("a"); }); - testApp.get('/timeout', function(req, res){ - setTimeout(function() { - res.send('hello'); - }, 10000); - }); - testApp.get('/timeout50ms', function(req, res){ - setTimeout(function() { - res.send('hello'); - }, 50); - }); - testApp.get('/checkCookie', function(req, res){ - res.send(req.cookies); - }); - testApp.get('/setCookie', function(req, res){ - res.cookie('data','hello'); - res.send(""); - }); - testApp.get('/authenticate', function(req, res){ - let result; - let authHeader = req.headers['authorization']; - if (/^Basic/.test(authHeader)) { - result = auth.parse(authHeader); - result.user = result.name; - } else if (/^Bearer/.test(authHeader)) { - result = { - token: authHeader.substring(7) - } - } - res.json(result); - }); - testApp.get('/proxyAuthenticate', function(req, res){ - // var user = auth.parse(req.headers['proxy-authorization']); - var result = { - //user: user.name, - //pass: user.pass, - headers: req.headers - }; - res.json(result); - }); - testApp.post('/postInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.put('/putInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.delete('/deleteInspect', function(req,res) { res.status(204).end();}); - testApp.head('/headInspect', function(req,res) { res.status(204).end();}); - testApp.patch('/patchInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.trace('/traceInspect', function(req,res) { - var result = { - body: req.body.toString(), - headers: req.headers - }; - res.json(result); - }); - testApp.options('/*', function(req,res) { - res.status(200).end(); - }); - testApp.get('/redirectToSameDomain', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectToSameDomainCookie','same1'); - res.redirect(getTestURL('/redirectReturn')); - }); - testApp.get('/redirectToDifferentDomain', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectToDifferentDomain','different1'); - res.redirect(getDifferentTestURL('/redirectReturn')); - }); - testApp.get('/redirectMultipleTimes', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectMultipleTimes','multiple1'); - res.redirect(getTestURL('/redirectToDifferentDomain')); - }); - testApp.get('/redirectReturn', function(req, res) { - var key = req.headers.host + req.url; - receivedCookies[key] = req.cookies; - res.cookie('redirectReturn','return1'); - res.status(200).end(); - }); - testApp.get('/getQueryParams', function(req,res) { - res.json({ - query:req.query, - url: req.originalUrl - }); - }) - testApp.get('/returnError/:code', function(req,res) { - res.status(parseInt(req.params.code)).json({gotError:req.params.code}); - }) - - testApp.get('/rawHeaders', function(req,res) { - const result = {}; - for (let i=0;i { - testProxyServer.stop(() => { - testProxyServerAuth.stop(() => { - testSslServer.stop(() => { - testSslClientServer.stop(() => { - helper.stopServer(done); - }) - }); - }); - }); - }); - }); - - beforeEach(function() { - preEnvHttpProxyLowerCase = process.env.http_proxy; - preEnvHttpProxyUpperCase = process.env.HTTP_PROXY; - preEnvNoProxyLowerCase = process.env.no_proxy; - preEnvNoProxyUpperCase = process.env.NO_PROXY; - process.env.no_proxy = 'localhost'; - process.env.NO_PROXY = 'localhost'; - }); - - afterEach(function() { - process.env.http_proxy = preEnvHttpProxyLowerCase; - process.env.HTTP_PROXY = preEnvHttpProxyUpperCase; - // On Windows, if environment variable of NO_PROXY that includes lower cases - // such as No_Proxy is replaced with NO_PROXY. - process.env.no_proxy = preEnvNoProxyLowerCase; - process.env.NO_PROXY = preEnvNoProxyUpperCase; - if (preEnvHttpProxyLowerCase == undefined) { - delete process.env.http_proxy; - } - if (preEnvHttpProxyUpperCase == undefined) { - delete process.env.HTTP_PROXY; - } - if (preEnvNoProxyLowerCase == undefined) { - delete process.env.no_proxy; - } - if (preEnvNoProxyUpperCase == undefined) { - delete process.env.NO_PROXY; - } - helper.unload(); - }); - - describe('request', function() { - it('should get plain text content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.redirectList.length.should.equal(0); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should get JSON content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-valid')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{a:1}); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should send the payload as the body of a POST as application/json', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('{"foo":"abcde"}'); - msg.payload.headers.should.have.property('content-type').which.startWith('application/json'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}}); - }); - }); - - it('should send a payload of 0 as the body of a POST as text/plain', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('0'); - msg.payload.headers.should.have.property('content-length','1'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:0, headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send an Object payload as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('{"foo":"abcde"}'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}, headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send a Buffer as the body of a POST', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('hello'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:Buffer.from('hello'), headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send form-based request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.body.should.equal("foo=1%202%203&bar="); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.have.property('content-type','application/x-www-form-urlencoded'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:'1 2 3', bar:''}, headers: { 'content-type': 'application/x-www-form-urlencoded'}}); - }); - }); - - it('should send PUT request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"PUT",ret:"obj",url:getTestURL('/putInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('foo'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send DELETE request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"DELETE",ret:"obj",url:getTestURL('/deleteInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{foo:"abcde"}}); - }); - }); - - it('should send HEAD request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",url:getTestURL('/headInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"head"}); - }); - }); - - it('should send PATCH request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"PATCH",ret:"obj",url:getTestURL('/patchInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('foo'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('etag'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should send OPTIONS request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/*')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"options"}); - }); - }); - - it('should send TRACE request', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"obj",url:getTestURL('/traceInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.body.should.eql('foo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"trace", headers: { 'content-type': 'text/plain'}}); - }); - }); - - it('should get Buffer content', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"bin",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should return plain text when JSON fails to parse', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/json-invalid')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',"{a:1"); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-type').which.startWith('application/json'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should return the status code', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/statusCode204')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',''); - msg.should.have.property('statusCode',204); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use msg.url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", url:"/foo"}); - }); - }); - - it('should output an error when URL is not provided', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:""}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo"}); - setTimeout(function() { - if (inError) { - done(new Error("no url allowed though")); - } else { - done(); - } - },20); - }); - }); - - it('should allow the message to provide the url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",url:getTestURL('/text')}); - }); - }); - - it('should allow the url to contain mustache placeholders', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/te{{placeholder}}')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",placeholder:"xt"}); - }); - }); - - it('should allow the url to be missing the http:// prefix', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text').substring("http://".length)}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should reject non http:// schemes - node config', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:"ftp://foo"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo"}); - setTimeout(function() { - if (inError) { - done(new Error("non http(s):// scheme allowed through")); - } else { - done(); - } - },20); - }); - }); - - it('should reject non http:// schemes - msg.url', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var inError = false; - n2.on("input", function(msg) { - inError = true; - }); - n1.receive({payload:"foo",url:"ftp://foo"}); - setTimeout(function() { - if (inError) { - done(new Error("non http(s):// scheme allowed through")); - } else { - done(); - } - },20); - }); - }); - - it('should use msg.method', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", method:"POST"}); - }); - }); - - it('should allow the message to provide the method', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",ret:"txt",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",method:"get"}); - }); - }); - - it('should receive msg.responseUrl', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.should.have.property('responseUrl', getTestURL('/text')); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should receive msg.responseUrl when redirected', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/redirectToText')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('responseUrl', getTestURL('/text')); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should prevent following redirect when msg.followRedirects is false', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/redirectToText')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',302); - msg.should.have.property('responseUrl', getTestURL('/redirectToText')); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo",followRedirects:false}); - }); - }); - - it('should output an error when request timeout occurred', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')}, - {id:"n2", type:"helper"}]; - var timeout = RED.settings.httpRequestTimeout; - RED.settings.httpRequestTimeout = 50; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode'); - /TIMEDOUT/.test(msg.statusCode).should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().ERROR, id:'n1',type:'http request',msg:'common.notification.errors.no-response', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } finally { - RED.settings.httpRequestTimeout = timeout; - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should output an error when request timeout occurred when set via msg.requestTimeout', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode'); - /TIMEDOUT/.test(msg.statusCode).should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(1); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().ERROR, id:'n1',type:'http request',msg:'common.notification.errors.no-response', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: 50}); - }); - }); - it('should show a warning if msg.requestTimeout is not a number', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode', 200); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(2); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnan', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: "foo"}); - }); - }); - it('should show a warning if msg.requestTimeout is negative', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode', 200); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(2); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnegative', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: -4}); - }); - }); - it('should show a warning if msg.requestTimeout is set to 0', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode', 200); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == 'http request'; - }); - logEvents.should.have.length(2); - var tstmp = logEvents[0][0].timestamp; - logEvents[0][0].should.eql({level:helper.log().WARN, id:'n1',type:'http request',msg:'httpin.errors.timeout-isnegative', timestamp:tstmp, path:"global"}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: 0}); - }); - }); - it('should pass if response time is faster than timeout set via msg.requestTimeout', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/timeout50ms')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", requestTimeout: 100}); - }); - }); - it('should append query params to url - obj', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",paytoqs:true,ret:"obj",url:getTestURL('/getQueryParams')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ - query:{a:'1',b:'2',c:'3'}, - url: '/getQueryParams?a=1&b=2&c=3' - }); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:{a:1,b:2,c:3}}); - }); - }); - - it('should send a msg for non-2xx response status - 400', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/400')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ gotError: '400' }); - msg.should.have.property('statusCode',400); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({}); - }) - }); - it('should send a msg for non-2xx response status - 404', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/404')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ gotError: '404' }); - msg.should.have.property('statusCode',404); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({}); - }) - }); - it('should send a msg for non-2xx response status - 500', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/500')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ gotError: '500' }); - msg.should.have.property('statusCode',500); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({}); - }) - }); - - it('should encode the url to handle special characters', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj"}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload',{ - query:{ a: 'b', c:[ 'T24,0°|H80%|W S8,3m/s' ] }, - url: '/getQueryParams?a=b&c[0].Text=T24,0%C2%B0|H80%25|W%20S8,3m/s' - }); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({url: getTestURL('/getQueryParams')+"?a=b&c[0].Text=T24,0°|H80%|W%20S8,3m/s"}); - }); - }) - }); - - describe('HTTP header', function() { - it('should receive cookie', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/setCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.responseCookies.should.have.property('data'); - msg.responseCookies.data.should.have.property('value','hello'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should send cookie with string', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:'abc'}}); - }); - }); - - it('should send multiple cookies with string', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.payload.should.have.property('foo','bar'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:'abc',foo:'bar'}}); - }); - }); - - it('should send cookie with object data', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:'abc'}}}); - }); - }); - - it('should send multiple cookies with object data', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.payload.should.have.property('foo','bar'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:'abc'},foo:{value:'bar'}}}); - }); - }); - - it('should encode cookie value', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var value = ';,/?:@ &=+$#'; - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data',encodeURIComponent(value)); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:value}}); - }); - }); - - it('should encode cookie object', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var value = ';,/?:@ &=+$#'; - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data',encodeURIComponent(value)); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:value, encode:true}}}); - }); - }); - - it('should not encode cookie when encode option is false', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var value = '!#$%&\'()*+-./:<>?@[]^_`{|}~'; - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data',value); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{data:{value:value, encode:false}}}); - }); - }); - - it('should send cookie by msg.headers', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{boo:'123'}, headers:{'cookie':'data=abc'}}); - }); - }); - - it('should send multiple cookies by msg.headers', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/checkCookie')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property('data','abc'); - msg.payload.should.have.property('foo','bar'); - msg.should.have.property('statusCode',200); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", cookies:{boo:'123'}, headers:{'cookie':'data=abc; foo=bar;'}}); - }); - }); - - it('should convert all HTTP headers into lower case', function(done) { - // This is a bad test. Express lower-cases headers in the `req.headers` object, - // so this is actually testing express, not the original request. - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.payload.headers.should.have.property('content-length', "3"); - msg.payload.headers.should.have.property('if-modified-since','Sun, 01 Jun 2000 00:00:00 GMT'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'Content-Type':'text/plain', 'Content-Length': "3", 'If-Modified-Since':'Sun, 01 Jun 2000 00:00:00 GMT'}}); - }); - }); - - it('should keep HTTP header case as provided by the user', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/rawHeaders')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.have.property('Content-Type').which.startWith('text/plain'); - msg.payload.headers.should.have.property('X-Test-HEAD', "foo"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", headers: { 'Content-Type':'text/plain', "X-Test-HEAD": "foo"}}); - }); - }); - it('should receive HTTP header', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/headersInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.headers.should.have.property('x-test-header','bar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should ignore unmodified x-node-red-request-node header', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.headers.should.have.property('content-type').which.startWith('application/json'); - msg.payload.headers.should.not.have.property('x-node-red-request-node'); - done(); - } catch(err) { - done(err); - } - }); - // Pass in a headers property with an unmodified x-node-red-request-node hash - // This should cause the node to ignore the headers - - var headers = { 'content-type': 'text/plain' }; - headers['x-node-red-request-node'] = require("hash-sum")(headers); - - n1.receive({payload:{foo:"bar"}, headers: headers}); - }); - }); - - it('should use modified msg.headers property', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.headers.should.have.property('content-type').which.startWith('text/plain'); - msg.payload.headers.should.not.have.property('x-node-red-request-node'); - done(); - } catch(err) { - done(err); - } - }); - // Pass in a headers property with a x-node-red-request-node hash that doesn't match the contents - // This should cause the node to use the headers - n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}}); - }); - }); - }); - - describe('protocol', function() { - it('should use msg.rejectUnauthorized', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo", rejectUnauthorized: false}); - }); - }); - - it('should use tls-config', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURLWithoutProtocol('/text'),tls:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"", verifyservercert:false}]; - var testNodes = [httpRequestNode, tlsNode]; - helper.load(testNodes, flow, function() { - var n3 = helper.getNode("n3"); - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use tls-config and verify serverCert', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text'),tls:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:true}]; - var testNodes = [httpRequestNode, tlsNode]; - helper.load(testNodes, flow, function() { - var n3 = helper.getNode("n3"); - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use tls-config and send client cert', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslClientTestURL('/getClientCert'),tls:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:false}]; - var testNodes = [httpRequestNode,tlsNode]; - helper.load(testNodes, flow, function() { - var n3 = helper.getNode("n3"); - var n2 = helper.getNode("n2"); - var n1 = helper.getNode("n1"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload','hello'); - msg.should.have.property('statusCode',200); - msg.should.have.property('headers'); - msg.headers.should.have.property('content-length',''+('hello'.length)); - msg.headers.should.have.property('content-type').which.startWith('text/html'); - msg.should.have.property('responseUrl').which.startWith('https://'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }) - }); - - //Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy - /* */ - it('should use http_proxy', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://localhost:" + testProxyPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - /* */ - - it('should use http_proxy when environment variable is invalid', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "invalidvalue"; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - // Remove HTTP-Proxy Authentication tests - /* */ - it('should use HTTP_PROXY', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.HTTP_PROXY = "http://localhost:" + testProxyPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* */ - - it('should use no_proxy', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://localhost:" + testProxyPort; - process.env.no_proxy = "foo,localhost"; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use NO_PROXY', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.HTTP_PROXY = "http://localhost:" + testProxyPort; - process.env.NO_PROXY = "foo,localhost"; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - // Remove HTTP-Proxy Authentication tests - /* */ - it('should use http-proxy-config', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect'),proxy:"n3"}, - {id:"n2",type:"helper"}, - {id:"n3",type:"http proxy",url:"http://localhost:" + testProxyPort} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* */ - - it('should not use http-proxy-config when invalid url is specified', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"invalidvalue"} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('headers'); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should use http-proxy-config when valid noproxy is specified', function(done) { - var flow = [ - {id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"http://localhost:" + testProxyPort,noproxy:["foo","localhost"]} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.headers.should.not.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - }); - describe('authentication', function() { - - it('should authenticate on server - basic', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/authenticate')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.credentials = {user:'userfoo', password:'passwordfoo'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('user', 'userfoo'); - msg.payload.should.have.property('pass', 'passwordfoo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - it('should authenticate on server - basic', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/authenticate')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.credentials = {user:'foo@example.com', password:'passwordfoo'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('user', 'foo@example.com'); - msg.payload.should.have.property('pass', 'passwordfoo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - it('should authenticate on server - bearer', function(done) { - var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"bearer", url:getTestURL('/authenticate')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.credentials = {password:'passwordfoo'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - msg.payload.should.have.property('token', 'passwordfoo'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - // Removed the Proxy Tests until a new mock proxy can be replaced with - // one that supports HTTP Connect verb - /* */ - it('should authenticate on proxy server', function(done) { - var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://foouser:barpassword@localhost:" + testProxyAuthPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - //msg.payload.should.have.property('user', 'foouser'); - //msg.payload.should.have.property('pass', 'barpassword'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* - it('should output an error when proxy authentication was failed', function(done) { - var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')}, - {id:"n2", type:"helper"}]; - deleteProxySetting(); - process.env.http_proxy = "http://xxxuser:barpassword@localhost:" + testProxyAuthPort; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',407); - msg.headers.should.have.property('proxy-authenticate', 'BASIC realm="proxy"'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - */ - it('should authenticate on proxy server(http-proxy-config)', function(done) { - var flow = [ - {id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"http://localhost:" + testProxyAuthPort} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - n3.credentials = {username:'foouser', password:'barpassword'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',200); - // msg.payload.should.have.property('user', 'foouser'); - // msg.payload.should.have.property('pass', 'barpassword'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - /* - it('should output an error when proxy authentication was failed(http-proxy-config)', function(done) { - var flow = [ - {id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate'),proxy:"n3"}, - {id:"n2", type:"helper"}, - {id:"n3",type:"http proxy",url:"http://@localhost:" + testProxyAuthPort} - ]; - var testNode = [ httpRequestNode, httpProxyNode ]; - deleteProxySetting(); - helper.load(testNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var n3 = helper.getNode("n3"); - n3.credentials = {username:'xxxuser', password:'barpassword'}; - n2.on("input", function(msg) { - try { - msg.should.have.property('statusCode',407); - msg.headers.should.have.property('proxy-authenticate', 'BASIC realm="proxy"'); - msg.payload.should.have.property('headers'); - //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:"foo"}); - }); - }); - */ - - }); - - describe('file-upload', function() { - it('should upload a file', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'POST',ret:'obj',url:getTestURL('/file-upload')}, - {id:"n2", type:"helper"}]; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property("body",{"other":"123"}); - msg.payload.should.have.property("files"); - msg.payload.files.should.have.length(1); - msg.payload.files[0].should.have.property('fieldname','file'); - msg.payload.files[0].should.have.property('originalname','file.txt'); - msg.payload.files[0].should.have.property('buffer',{"type":"Buffer","data":[72,101,108,108,111,32,87,111,114,108,100]}); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({ - headers: { - 'content-type':'multipart/form-data' - }, - payload: { - file: { - value: Buffer.from("Hello World"), - options: { - filename: "file.txt" - } - }, - other: 123 - } - }); - }); - }) - }) - - describe('redirect-cookie', function() { - it('should send cookies to the same domain when redirected(no cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToSameDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['localhost:'+testPort+'/redirectReturn']; - if (cookies1 && Object.keys(cookies1).length != 0) { - done(new Error('Invalid cookie(path:/rediectToSame)')); - return; - } - if ((cookies2 && Object.keys(cookies2).length != 1) || - cookies2['redirectToSameDomainCookie'] !== 'same1') { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToSameDomainCookie.Path.should.equal('/'); - redirect1.cookies.redirectToSameDomainCookie.value.should.equal('same1'); - done(); - } catch(err) { done(err)} - }); - n1.receive({}); - }); - }); - it('should not send cookies to the different domain when redirected(no cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToDifferentDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['127.0.0.1:'+testPort+'/redirectReturn']; - if (cookies1 && Object.keys(cookies1).length != 0) { - done(new Error('Invalid cookie(path:/rediectToDiffer)')); - return; - } - if (cookies2 && Object.keys(cookies2).length != 0) { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect1.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({}); - }); - }); - it('should send cookies to the same domain when redirected(msg.cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToSameDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['localhost:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToSame)')); - return; - } - if ((cookies2 && Object.keys(cookies2).length != 2) || - cookies1['requestCookie'] !== 'request1' || - cookies2['redirectToSameDomainCookie'] !== 'same1') { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToSameDomainCookie.Path.should.equal('/'); - redirect1.cookies.redirectToSameDomainCookie.value.should.equal('same1'); - done(); - }); - n1.receive({ - cookies: { requestCookie: 'request1' } - }); - }); - }); - it('should not send cookies to the different domain when redirected(msg.cookies)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToDifferentDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToDifferentDomain']; - var cookies2 = receivedCookies['127.0.0.1:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToDiffer)')); - return; - } - if (cookies2 && Object.keys(cookies2).length != 0) { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect1.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({ - cookies: { requestCookie: 'request1' } - }); - }); - }); - it('should send cookies to the same domain when redirected(msg.headers.cookie)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToSameDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToSameDomain']; - var cookies2 = receivedCookies['localhost:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToSame)')); - return; - } - if ((cookies2 && Object.keys(cookies2).length != 2) || - cookies1['requestCookie'] !== 'request1' || - cookies2['redirectToSameDomainCookie'] !== 'same1') { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToSameDomainCookie.Path.should.equal('/'); - redirect1.cookies.redirectToSameDomainCookie.value.should.equal('same1'); - done(); - }); - n1.receive({ - headers: { cookie: 'requestCookie=request1' } - }); - }); - }); - it('should not send cookies to the different domain when redirected(msg.headers.cookie)', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectToDifferentDomain')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var cookies1 = receivedCookies['localhost:'+testPort+'/redirectToDifferentDomain']; - var cookies2 = receivedCookies['127.0.0.1:'+testPort+'/redirectReturn']; - if ((cookies1 && Object.keys(cookies1).length != 1) || - cookies1['requestCookie'] !== 'request1') { - done(new Error('Invalid cookie(path:/rediectToDiffer)')); - return; - } - if (cookies2 && Object.keys(cookies2).length != 0) { - done(new Error('Invalid cookie(path:/rediectReurn)')); - return; - } - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect1.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect1.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({ - headers: { cookie: 'requestCookie=request1' } - }); - }); - }); - it('should return all redirect information when redirected multiple times', function(done) { - var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:getTestURL('/redirectMultipleTimes')}, - {id:"n2", type:"helper"}]; - receivedCookies = {}; - helper.load(httpRequestNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - var redirect1 = msg.redirectList[0]; - redirect1.location.should.equal('http://localhost:'+testPort+'/redirectToDifferentDomain'); - redirect1.cookies.redirectMultipleTimes.Path.should.equal('/'); - redirect1.cookies.redirectMultipleTimes.value.should.equal('multiple1'); - var redirect2 = msg.redirectList[1]; - redirect2.location.should.equal('http://127.0.0.1:'+testPort+'/redirectReturn'); - redirect2.cookies.redirectToDifferentDomain.Path.should.equal('/'); - redirect2.cookies.redirectToDifferentDomain.value.should.equal('different1'); - done(); - }); - n1.receive({ - headers: { cookie: 'requestCookie=request1' } - }); - }); - }); - }); -}); diff --git a/test/nodes/core/network/22-websocket_spec.js b/test/nodes/core/network/22-websocket_spec.js deleted file mode 100644 index 995248279..000000000 --- a/test/nodes/core/network/22-websocket_spec.js +++ /dev/null @@ -1,568 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var ws = require("ws"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var websocketNode = require("nr-test-utils").require("@node-red/nodes/core/network/22-websocket.js"); - -var sockets = []; - -function getWsUrl(path) { - return helper.url().replace(/http/, "ws") + path; -} - -function createClient(listenerid) { - return new Promise(function(resolve, reject) { - var node = helper.getNode(listenerid); - var url = getWsUrl(node.path); - var sock = new ws(url); - sockets.push(sock); - - sock.on("open", function() { - resolve(sock); - }); - - sock.on("error", function(err) { - reject(err); - }); - }); -} - -function closeAll() { - for (var i = 0; i < sockets.length; i++) { - sockets[i].close(); - } - sockets = []; -} - -function getSocket(listenerid) { - var node = helper.getNode(listenerid); - return node.server; -} - -describe('websocket Node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - closeAll(); - helper.unload(); - }); - - describe('websocket-listener', function() { - it('should load', function(done) { - var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property("path", "/ws"); - done(); - }); - }); - - it('should be server', function(done) { - var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property('isServer', true); - done(); - }); - }); - - it('should handle wholemsg property', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket-listener", path: "/ws2", wholemsg: "true" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property("wholemsg", false); - helper.getNode("n2").should.have.property("wholemsg", true); - done(); - }); - }); - - it('should create socket', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket in", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should close socket on delete', function(done) { - var flow = [{ id: "n1", type: "websocket-listener", path: "/ws" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("close", function(code, msg) { - done(); - }); - helper.clearFlows(); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive data', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - sock.send("hello"); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive wholemsg', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.send('{"text":"hello"}'); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("text", "hello"); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive wholemsg when data not JSON', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.send('hello'); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should receive wholemsg when data not object', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", 123); - done(); - }); - sock.send(123); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should send', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "helper", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - msg.should.equal("hello"); - done(); - }); - helper.getNode("n2").send({ - payload: "hello" - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should send wholemsg', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket out", server: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - JSON.parse(msg).should.have.property("text", "hello"); - done(); - }); - helper.getNode("n3").send({ - text: "hello" - }); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should do nothing if no payload', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "helper", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - setTimeout(function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - logEvents.should.have.length(0); - done(); - },100); - helper.getNode("n2").send({topic: "hello"}); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should echo', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - msg.should.equal("hello"); - done(); - }); - sock.send("hello"); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should echo wholemsg', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "n2", type: "websocket in", server: "n1", wires: [["n3"]] }, - { id: "n3", type: "websocket out", server: "n1" }]; - helper.load(websocketNode, flow, function() { - createClient("n1").then(function(sock) { - sock.on("message", function(msg, flags) { - JSON.parse(msg).should.have.property("text", "hello"); - done(); - }); - sock.send('{"text":"hello"}'); - }).catch(function(err) { - done(err); - }); - }); - }); - - it('should broadcast', function(done) { - var flow = [ - { id: "n1", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket out", server: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - Promise.all([createClient("n1"), createClient("n1")]).then(function(socks) { - var promises = [ - new Promise((resolve,reject) => { - socks[0].on("message", function(msg, flags) { - try { - msg.should.equal("hello"); - resolve(); - } catch(err) { - reject(err); - } - }); - }), - new Promise((resolve,reject) => { - socks[1].on("message", function(msg, flags) { - try { - msg.should.equal("hello"); - resolve(); - } catch(err) { - reject(err); - } - }); - }) - ]; - helper.getNode("n3").send({ - payload: "hello" - }); - return Promise.all(promises).then(() => {done()}); - }).catch(function(err) { - done(err); - }); - }); - }); - }); - - describe('websocket-client', function() { - it('should load', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property('path', getWsUrl("/ws")); - done(); - }); - }); - - it('should not be server', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property('isServer', false); - done(); - }); - }); - - it('should handle wholemsg property', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }]; - helper.load(websocketNode, flow, function() { - helper.getNode("n1").should.have.property("wholemsg", false); - helper.getNode("n2").should.have.property("wholemsg", true); - done(); - }); - }); - - it('should connect to server', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - done(); - }); - - }); - }); - - it('should close on delete', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n2", type: "websocket-client", path: getWsUrl("/ws") }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('close', function() { - done(); - }); - helper.getNode("n2").close(); - }); - }); - }); - - it('should receive data', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket in", client: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.send('hello'); - }); - - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - }); - }); - - it('should receive wholemsg data ', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n2", type: "websocket in", client: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.send('{"text":"hello"}'); - }); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("text", "hello"); - done(); - }); - }); - }); - - it('should receive wholemsg when data not JSON', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n2", type: "websocket in", client: "n1", wires: [["n3"]] }, - { id: "n3", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.send('hello'); - }); - helper.getNode("n3").on("input", function(msg) { - msg.should.have.property("payload", "hello"); - done(); - }); - }); - }); - - it('should send', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket out", client: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('message', function(msg) { - msg.should.equal("hello"); - done(); - }); - }); - getSocket("n1").on("open", function() { - helper.getNode("n3").send({ - payload: "hello" - }); - }); - }); - }); - - it('should send buffer', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws") }, - { id: "n2", type: "websocket out", client: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('message', function(msg) { - Buffer.isBuffer(msg).should.be.true(); - msg.should.have.length(5); - done(); - }); - }); - getSocket("n1").on("open", function() { - helper.getNode("n3").send({ - payload: Buffer.from("hello") - }); - }); - }); - }); - - it('should send wholemsg', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws" }, - { id: "n1", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n2", type: "websocket out", client: "n1" }, - { id: "n3", type: "helper", wires: [["n2"]] }]; - helper.load(websocketNode, flow, function() { - getSocket('server').on('connection', function(sock) { - sock.on('message', function(msg) { - JSON.parse(msg).should.have.property("text", "hello"); - done(); - }); - }); - getSocket("n1").on('open', function(){ - helper.getNode("n3").send({ - text: "hello" - }); - }); - }); - }); - - it('should NOT feedback more than once', function(done) { - var flow = [ - { id: "server", type: "websocket-listener", path: "/ws", wholemsg: "true" }, - { id: "client", type: "websocket-client", path: getWsUrl("/ws"), wholemsg: "true" }, - { id: "n1", type: "websocket in", client: "client", wires: [["n2", "output"]] }, - { id: "n2", type: "websocket out", server: "server" }, - { id: "n3", type: "helper", wires: [["n2"]] }, - { id: "output", type: "helper" }]; - helper.load(websocketNode, flow, function() { - getSocket('client').on('open', function() { - helper.getNode("n3").send({ - payload: "ping" - }); - }); - var acc = 0; - helper.getNode("output").on("input", function(msg) { - acc = acc + 1; - }); - setTimeout( function() { - acc.should.equal(1); - helper.clearFlows(); - done(); - }, 250); - }); - }); - }); - - describe('websocket in node', function() { - it('should report error if no server config', function(done) { - var flow = [{ id: "n1", type: "websocket in", mode: "server" }]; - helper.load(websocketNode, flow, function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "websocket in"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("websocket.errors.missing-conf"); - done(); - }); - }); - }); - - describe('websocket out node', function() { - it('should report error if no server config', function(done) { - var flow = [{ id: "n1", type: "websocket out", mode: "server" }]; - helper.load(websocketNode, flow, function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "websocket out"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("websocket.errors.missing-conf"); - done(); - }); - }); - }); -}); diff --git a/test/nodes/core/network/31-tcpin_spec.js b/test/nodes/core/network/31-tcpin_spec.js deleted file mode 100644 index cbe7ced2a..000000000 --- a/test/nodes/core/network/31-tcpin_spec.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var should = require("should"); -var stoppable = require('stoppable'); -var helper = require("node-red-node-test-helper"); - -var tcpinNode = require("nr-test-utils").require("@node-red/nodes/core/network/31-tcpin.js"); - - -describe('TCP in Node', function() { - var port = 9200; - var server = undefined; - var server_port = 9300; - var reply_data = undefined; - - beforeEach(function(done) { - startServer(done); - }); - - afterEach(function(done) { - helper.unload(); - stopServer(done); - }); - - function sendArray(sock, array) { - if(array.length > 0) { - sock.write(array[0], function() { - sendArray(sock, array.slice(1)); - }); - } - else { - sock.end(); - } - } - - function startServer(done) { - server_port += 1; - server = stoppable(net.createServer(function(c) { - sendArray(c, reply_data); - })).listen(server_port, "localhost", function(err) { - done(err); - }); - } - - function stopServer(done) { - server.stop(done); - } - - function send(wdata) { - var opt = {port:port, host:"localhost"}; - var client = net.createConnection(opt, function() { - client.write(wdata[0], function() { - client.end(); - if(wdata.length > 1) { - send(wdata.slice(1)); - } - }); - }); - } - - function eql(v0, v1) { - return((v0 === v1) || ((typeof v0) === 'object' && v0.equals(v1))); - } - - function testTCP(flow, wdata, rdata, is_server, done) { - if(is_server) { - reply_data = wdata; - } - helper.load(tcpinNode, flow, function() { - var n2 = helper.getNode("n2"); - var rcount = 0; - n2.on("input", function(msg) { - if(eql(msg.payload, rdata[rcount])) { - rcount++; - } - else { - should.fail(); - } - if(rcount === rdata.length) { - done(); - } - }); - if(!is_server) { - send(wdata); - } - }); - } - - function testTCP0(flow, wdata, rdata, done) { - testTCP(flow, wdata, rdata, false, done); - } - - function testTCP1(flow, wdata, rdata, done) { - testTCP(flow, wdata, rdata, true, done); - } - - it('should recv data (Stream/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should recv data (Stream/String/Delimiter:\\n)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo\nbar"], ["foo", "bar"], done); - }); - - it('should recv data (Stream/String/No delimiter)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo\nbar"], ["foo\nbar"], done); - }); - - it('should recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - - it('should recv data (Single/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should recv data (Single/String)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo\nbar\nbaz"], ["foo\nbar\nbaz"], done); - }); - - it('should recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"single", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - - it('should recv multiple data (Stream/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo", "bar"], [Buffer("foo"), Buffer("bar")], done); - }); - - it('should recv multiple data (Stream/String/Delimiter:\\n)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo", "bar\nbaz"], ["foo", "bar", "baz"], done); - }); - - it('should recv multiple data (Stream/String/No delimiter)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP0(flow, ["foo", "bar\nbaz"], ["foo", "bar\nbaz"], done); - }); - - it('should recv multiple data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"server", host:"localhost", port:port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - var wdata = ["foo", "bar"]; - var rdata = wdata.map(function(x) { - return Buffer(x).toString('base64'); - }); - testTCP0(flow, wdata, rdata, done); - }); - - it('should connect & recv data (Stream/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should connect & recv data (Stream/String/Delimiter:\\n)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo\nbar"], ["foo", "bar"], done); - }); - - it('should connect & recv data (Stream/String/No delimiter)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"utf8", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo\nbar"], ["foo\nbar"], done); - }); - - it('should connect & recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"stream", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - - it('should connect & recv data (Single/Buffer)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"buffer", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo")], done); - }); - - it('should connect & recv data (Single/String)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"utf8", newline:"\n", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo\nbar\nbaz"], ["foo\nbar\nbaz"], done); - }); - - it('should connect & recv data (Stream/Base64)', function(done) { - var flow = [{id:"n1", type:"tcp in", server:"client", host:"localhost", port:server_port, datamode:"single", datatype:"base64", newline:"", topic:"", base64:false, wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP1(flow, ["foo"], [Buffer("foo").toString('base64')], done); - }); - -}); diff --git a/test/nodes/core/network/31-tcprequest_spec.js b/test/nodes/core/network/31-tcprequest_spec.js deleted file mode 100644 index dc0755c1b..000000000 --- a/test/nodes/core/network/31-tcprequest_spec.js +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var should = require("should"); -var stoppable = require('stoppable'); -var helper = require("node-red-node-test-helper"); -var tcpinNode = require("nr-test-utils").require("@node-red/nodes/core/network/31-tcpin.js"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); - - -describe('TCP Request Node', function() { - var server = undefined; - var port = 9000; - - function startServer(done) { - port += 1; - server = stoppable(net.createServer(function(c) { - c.on('data', function(data) { - var rdata = "ACK:"+data.toString(); - c.write(rdata); - }); - c.on('error', function(err) { - startServer(done); - }); - })).listen(port, "127.0.0.1", function(err) { - done(); - }); - } - - before(function(done) { - startServer(done); - }); - - after(function(done) { - server.stop(done); - }); - - afterEach(function() { - helper.unload(); - }); - - function testTCP(flow, val0, val1, done) { - helper.load(tcpinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - if (typeof val1 === 'object') { - msg.should.have.properties(Object.assign({}, val1, {payload: Buffer.from(val1.payload)})); - } else { - msg.should.have.property('payload', Buffer.from(val1)); - } - done(); - } catch(err) { - done(err); - } - }); - if((typeof val0) === 'object') { - n1.receive(val0); - } else { - n1.receive({payload:val0}); - } - }); - } - - function testTCPMany(flow, values, result, done) { - helper.load(tcpinNode, flow, () => { - const n1 = helper.getNode("n1"); - const n2 = helper.getNode("n2"); - n2.on("input", msg => { - try { - if (typeof result === 'object') { - msg.should.have.properties(Object.assign({}, result, {payload: Buffer.from(result.payload)})); - } else { - msg.should.have.property('payload', Buffer.from(result)); - } - done(); - } catch(err) { - done(err); - } - }); - values.forEach(value => { - n1.receive(typeof value === 'object' ? value : {payload: value}); - }); - }); - } - - describe('single message', function () { - it('should send & recv data', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should retain complete message', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & recv data when specified character received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo0bar0', - topic: 'bar' - }, { - payload: 'ACK:foo0', - topic: 'bar' - }, done); - }); - - it('should send & recv data after fixed number of chars received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo bar', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & receive, then keep connection', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: 'foo', - topic: 'bar' - }, { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & recv data to/from server:port from msg', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCP(flow, { - payload: "foo", - host: "localhost", - port: port - }, { - payload: "ACK:foo", - host: 'localhost', - port: port - }, done); - }); - }); - - describe('many messages', function () { - it('should send & recv data', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: 'f', - topic: 'bar' - }, { - payload: 'o', - topic: 'bar' - }, { - payload: 'o', - topic: 'bar' - }], { - payload: 'ACK:foo', - topic: 'bar' - }, done); - }); - - it('should send & recv data when specified character received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "foo0", - topic: 'bar' - }, { - payload: "bar0", - topic: 'bar' - }], { - payload: "ACK:foo0", - topic: 'bar' - }, done); - }); - - it('should send & recv data after fixed number of chars received', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "fo", - topic: 'bar' - }, { - payload: "ob", - topic: 'bar' - }, { - payload: "ar", - topic: 'bar' - }], { - payload: "ACK:foo", - topic: 'bar' - }, done); - }); - - it('should send & receive, then keep connection', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "foo", - topic: 'bar' - }, { - payload: "bar", - topic: 'bar' - }, { - payload: "baz", - topic: 'bar' - }], { - payload: "ACK:foobarbaz", - topic: 'bar' - }, done); - }); - - it('should send & recv data to/from server:port from msg', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: "f", - host: "localhost", - port: port - }, - { - payload: "o", - host: "localhost", - port: port - }, - { - payload: "o", - host: "localhost", - port: port - } - ], { - payload: "ACK:foo", - host: 'localhost', - port: port - }, done); - }); - - it('should limit the queue size', function (done) { - RED.settings.tcpMsgQueueSize = 10; - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - // create one more msg than is allowed - const msgs = new Array(RED.settings.tcpMsgQueueSize + 1).fill('x'); - const expected = msgs.slice(0, -1); - testTCPMany(flow, msgs, "ACK:" + expected.join(''), done); - }); - - it('should only retain the latest message', function(done) { - var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - testTCPMany(flow, [{ - payload: 'f', - topic: 'bar' - }, { - payload: 'o', - topic: 'baz' - }, { - payload: 'o', - topic: 'quux' - }], { - payload: 'ACK:foo', - topic: 'quux' - }, done); - }); - }); -}); diff --git a/test/nodes/core/network/32-udpin_spec.js b/test/nodes/core/network/32-udpin_spec.js deleted file mode 100644 index 107c313bf..000000000 --- a/test/nodes/core/network/32-udpin_spec.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var dgram = require("dgram"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var udpNode = require("nr-test-utils").require("@node-red/nodes/core/network/32-udp.js"); - - -describe('UDP in Node', function() { - var port = 9100; - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - function sendIPv4(msg) { - var sock = dgram.createSocket('udp4'); - sock.send(msg, 0, msg.length, port, "127.0.0.1", function(msg) { - sock.close(); - }); - } - - function checkRecv(dt, proto, val0, val1, done) { - var flow = [{id:"n1", type:"udp in", - group: "", multicast:false, - port:port, ipv:proto, - datatype: dt, iface: "", - wires:[["n2"]] }, - {id:"n2", type:"helper"}]; - helper.load(udpNode, flow, function() { - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - var ip = ((proto === 'udp6') ? '::ffff:':'') +'127.0.0.1'; - msg.should.have.property('ip', ip); - msg.should.have.property('port'); - msg.should.have.property('payload'); - msg.payload.should.deepEqual(val1); - done(); - } catch(err) { - done(err); - } - }); - sendIPv4(val0); - }); - } - - it('should recv IPv4 data (Buffer)', function(done) { - checkRecv('buffer', 'udp4', 'hello', Buffer('hello'), done); - }); - - it('should recv IPv4 data (String)', function(done) { - checkRecv('utf8', 'udp4', 'hello', 'hello', done); - }); - - it('should recv IPv4 data (base64)', function(done) { - checkRecv('base64', 'udp4', 'hello', Buffer('hello').toString('base64'), done); - }); - - it('should recv IPv6 data (Buffer)', function(done) { - checkRecv('buffer', 'udp6', 'hello', Buffer('hello'), done); - }); - - it('should recv IPv6 data (String)', function(done) { - checkRecv('utf8', 'udp6', 'hello', 'hello', done); - }); - - it('should recv IPv6 data (base64)', function(done) { - checkRecv('base64', 'udp6', 'hello', Buffer('hello').toString('base64'), done); - }); - -}); diff --git a/test/nodes/core/network/32-udpout_spec.js b/test/nodes/core/network/32-udpout_spec.js deleted file mode 100644 index ff01c2f10..000000000 --- a/test/nodes/core/network/32-udpout_spec.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var dgram = require("dgram"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var udpNode = require("nr-test-utils").require("@node-red/nodes/core/network/32-udp.js"); - - -describe('UDP out Node', function() { - var port = 9200; - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - function recvData(data, done) { - var sock = dgram.createSocket('udp4'); - sock.on('message', function(msg, rinfo) { - sock.close(done); - msg.should.deepEqual(data); - }); - sock.bind(port, '127.0.0.1'); - port++; - } - - function checkSend(proto, val0, val1, decode, dest_in_msg, done) { - var dst_ip = dest_in_msg ? undefined : "127.0.0.1"; - var dst_port = dest_in_msg ? undefined : port; - var flow = [{id:"n1", type:"udp out", - addr:dst_ip, port:dst_port, iface: "", - ipv:proto, outport: "", - base64:decode, multicast:false, - wires:[] }]; - helper.load(udpNode, flow, function() { - var n1 = helper.getNode("n1"); - var msg = {}; - if (decode) { - msg.payload = Buffer.from("hello").toString('base64'); - } - else { - msg.payload = "hello"; - } - if (dest_in_msg) { - msg.ip = "127.0.0.1"; - msg.port = port; - } - recvData(val1, done); - setTimeout(function() { - n1.receive(msg); - }, 200); - }); - } - - it('should send IPv4 data', function(done) { - checkSend('udp4', 'hello', Buffer.from('hello'), false, false, done); - }); - - it('should send IPv4 data (base64)', function(done) { - checkSend('udp4', 'hello', Buffer.from('hello'), true, false, done); - }); - - it('should send IPv4 data with dest from msg', function(done) { - checkSend('udp4', 'hello', Buffer.from('hello'), false, true, done); - }); - -}); diff --git a/test/nodes/core/parsers/70-CSV_spec.js b/test/nodes/core/parsers/70-CSV_spec.js deleted file mode 100644 index cb8d7ca09..000000000 --- a/test/nodes/core/parsers/70-CSV_spec.js +++ /dev/null @@ -1,1062 +0,0 @@ -/* eslint-disable no-undef */ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -// var should = require("should"); -var csvNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-CSV.js"); -var helper = require("node-red-node-test-helper"); -// const { neq } = require("semver"); - -describe('CSV node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded with defaults', function(done) { - var flow = [{id:"csvNode1", type:"csv", name: "csvNode" }]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("csvNode1"); - n1.should.have.property('name', 'csvNode'); - n1.should.have.property('template',''); - n1.should.have.property('sep', ','); - n1.should.have.property('quo', '"'); - n1.should.have.property('ret', '\n'); - n1.should.have.property('winflag', false); - n1.should.have.property('lineend', '\n'); - n1.should.have.property('multi', 'one'); - n1.should.have.property('hdrin', false); - done(); - }); - }); - - describe('csv to json', function() { - var parts_id = undefined; - - afterEach(function() { - parts_id = undefined; - }); - - function check_parts(msg, index, count) { - msg.should.have.property('parts'); - if(parts_id === undefined) { - parts_id = msg.parts.id; - } - else { - msg.parts.should.have.property('id', parts_id); - } - msg.parts.should.have.property('index', index); - msg.parts.should.have.property('count', count); - } - - it('should convert a simple csv string to a javascript object', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); - msg.should.have.property('columns', "a,b,c,d"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should convert a simple string to a javascript object with | separator (no template)', function(done) { - var flow = [ { id:"n1", type:"csv", sep:"|", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { col1: 1, col2: 2, col3: 3, col4: 4 }); - msg.should.have.property('columns', "col1,col2,col3,col4"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1|2|3|4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should convert a simple string to a javascript object with tab separator (with template)', function(done) { - var flow = [ { id:"n1", type:"csv", sep:"\t", temp:"A,B,,D", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { A: 1, B: 2, D: 4 }); - msg.should.have.property('columns', "A,B,D"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1\t2\t3\t4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should convert a simple string to a javascript object with space separator (with spaced template)', function(done) { - var flow = [ { id:"n1", type:"csv", sep:" ", temp:"A, B, , D", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { A: 1, B: 2, D: 4 }); - msg.should.have.property('columns', "A,B,D"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1 2 3 4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should remove quotes and whitespace from template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:'"a", "b" , " c "," d " ', wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should create column names if no template provided', function(done) { - var flow = [ { id:"n1", type:"csv", temp:'', wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { col1: 1, col2: 2, col3: 3, col4: 4 }); - msg.should.have.property('columns', "col1,col2,col3,col4"); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow dropping of fields from the template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,,,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, d: 4 }); - msg.should.have.property('columns', 'a,d'); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow commas and spaces in the template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b b,\"c,c\",\" d, d \"", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,"c,c","d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c,c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,"c,c","d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a,b b,"c,c"," d, d "'+"\n"+"1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV (not comma)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, sep:";", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c;c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,c;c,"d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a;b b;"c;c";" d, d "'+"\n"+"1;2;3;4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV (special char /)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, sep:"/", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c/c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,c/c,"d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a/b b/"c/c"/" d, d "'+"\n"+"1/2/3/4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow passing in a template as first line of CSV (special char \\)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, sep:"\\", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, "b b":2, "c\\c":3, "d, d": 4 }); - msg.should.have.property('columns', 'a,b b,c\\c,"d, d"'); - check_parts(msg, 0, 1); - done(); - }); - var testString = 'a\\b b\\"c\\c"\\" d, d "'+"\n"+"1\\2\\3\\4"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should leave numbers starting with 0, e and + as strings (except 0.)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 123, b: "0123", c: '+123', d: 'e123', e: 'E123', f: -123 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '123,0123,+123,e123,E123,-123'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should not parse numbers when told not to do so', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", strings:false, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: "1.23", b: "0123", c: "+123", d: "e123", e: "0", f: "-123", g: "1e3" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '1.23,0123,+123,e123,0,-123,1e3'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should parse numbers when told to do so', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1.23, b: -123, c: 1000, d: 0 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = ' 1.23 , -123,1e3 , 0 '+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should leave handle strings with scientific notation as numbers', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 12000, b: 0.012, c: -12000, d: -0.012 }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '12E3,12e-3,-12e3,-12E-3'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - - it('should allow quotes in the input (but drop blank strings)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g,h", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a:1, b:-2, c:'+3', d:'04', f:'-05', g:'ab"cd', h:'with,a,comma' }); - check_parts(msg, 0, 1); - done(); - }); - var testString = '"1","-2","+3","04","","-05","ab""cd","with,a,comma"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow blank strings in the input if selected', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", include_empty_strings:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a: 1, b: '', c: '', d: '', e: '-05', f: 'ab"cd', g: 'with,a,comma' }); - //check_parts(msg, 0, 1); - done(); - }); - var testString = '"1","","","","-05","ab""cd","with,a,comma"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should allow missing columns (nulls) in the input if selected', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", include_null_values:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a: 1, b: null, c: '+3', d: null, e: '-05', f: 'ab"cd', g: 'with,a,comma' }); - //check_parts(msg, 0, 1); - done(); - }); - var testString = '"1",,"+3",,"-05","ab""cd","with,a,comma"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should handle cr and lf in the input', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - //console.log(msg); - msg.should.have.property('payload', { a: "with a\nnew line", b: "and a\rcarriage return", c: "and why\r\nnot both"}); - check_parts(msg, 0, 1); - done(); - }); - var testString = '"with a'+String.fromCharCode(10)+'new line","and a'+String.fromCharCode(13)+'carriage return","and why\r\nnot both"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should recover from an odd number of quotes in the input', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c == 0) { - c = 1; - msg.should.have.property('payload', { a: "with,an", b: "odd,number", c: "ofquotes\n" }); - check_parts(msg, 0, 1); - } - else { - msg.should.have.property('payload', { a: "this is", b: "a normal", c: "line" }); - check_parts(msg, 0, 1); - done(); - } - }); - var testString = '"with,a"n,odd","num"ber","of"qu"ot"es"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - n1.emit("input", {payload:'"this is","a normal","line"'}); - }); - }); - - it('should recover from an odd number of quotes in the input (2)', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e,f,g", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - //console.log(msg) - if (c == 0) { - c = 1; - msg.should.have.property('payload', { a: "with,an", b: "odd,number", c: "ofquotes\nthis is,a normal,line" }); - check_parts(msg, 0, 1); - } - else { - msg.should.have.property('payload', { a: "this is", b: "another", c: "line" }); - check_parts(msg, 0, 1); - done(); - } - }); - var testString = '"with,a"n,odd","num"ber","of"qu"ot"es"'+String.fromCharCode(10)+'"this is","a normal","line"'+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - n1.emit("input", {payload:'"this is","another","line"'}); - }); - }); - - it('should be able to use the first line as a template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrin:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - //console.log(msg); - if (c === 0) { - msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); - check_parts(msg, 0, 2); - c += 1; - } - else { - msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); - check_parts(msg, 1, 2); - done(); - } - }); - var testString = "w,x,y,z\n1,2,3,4\n\n5,6,7,8"; - n1.emit("input", {payload:testString}); - }); - }); - - it('should be able to output multiple lines as one array', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", multi:"yes", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', [ { a: 1, b: 2, c: 3, d: 4 },{ a: 5, b: -6, c: '07', d: '+8' },{ a: 9, b: 0, c: 'a', d: 'b' },{ a: 'c', b: 'd', c: 'e', d: 'f' } ]); - msg.should.have.property('columns','a,b,c,d'); - msg.should.not.have.property('parts'); - done(); - }); - var testString = "1,2,3,4\n5,-6,07,+8\n9,0,a,b\nc,d,e,f"; - n1.emit("input", {payload:testString}); - }); - }); - - it('should be able to create an array from multiple parts', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, multi:"mult", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', [{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]); - msg.should.have.property('columns','a,b,c'); - msg.should.not.have.property('parts'); - done(); - }); - - n1.emit("input", {"payload":"a,b,c","parts":{"index":0,"ch":"\n","type":"string","id":"1"}}); - n1.emit("input", {"payload":"1,2,3","parts":{"index":1,"ch":"\n","type":"string","id":"1"}}); - n1.emit("input", {"payload":"4,5,6","parts":{"index":2,"ch":"\n","type":"string","id":"1"}}); - n1.emit("input", {"payload":"7,8,9","parts":{"index":3,count:4,"ch":"\n","type":"string","id":"1"}}); - }); - }); - - it('should be able to output multiple objects as an array from an input of parts', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, multi:"yes", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', [{"Col1":"V1","Col2":"V2"},{"Col1":"V3","Col2":"V4"},{"Col1":"V5","Col2":"V6"}]); - msg.should.have.property('columns','Col1,Col2'); - msg.should.have.property('parts'); - done(); - }); - //var testString = "1,2,3,4\n5,-6,07,+8\n9,0,a,b\nc,d,e,f"; - // n1.emit("input", {payload:testString}); - n1.emit("input", {"payload":"Col1,Col2\nV1,V2\nV3,V4\nV5,V6","topic":"","parts":{"id":"3af07e18.865652","type":"array","count":2,"len":1,"index":0}}); - //n1.emit("input", {"payload":"Var1,Var2\nW1,W2\nW3,W4\nW5,W6","topic":"","parts":{"id":"3af07e18.865652","type":"array","count":2,"len":1,"index":1}}); - }); - }); - - it('should handle numbers in strings but not IP addresses', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d,e", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: "a", b: "127.0.0.1", c: 56.7, d: -32.8, e: "+76.22C" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "a,127.0.0.1,56.7,-32.8,+76.22C"; - n1.emit("input", {payload:testString}); - }); - }); - - it('should preserve parts property', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 1, b: 2, c: 3, d: 4 }); - check_parts(msg, 3, 4); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10); - n1.emit("input", {payload:testString, parts: {id:"X", index:3, count:4} }); - }); - }); - - it('should be able to use the first of multiple parts as a template if parts are present', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); - check_parts(msg, 0, 2); - c += 1; - } - else { - msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); - check_parts(msg, 1, 2); - done(); - } - }); - var testString1 = "w,x,y,z\n"; - var testString2 = "1,2,3,4\n"; - var testString3 = "5,6,7,8\n"; - n1.emit("input", {payload:testString1, parts:{id:"X", index:0, count:3}}); - n1.emit("input", {payload:testString2, parts:{id:"X", index:1, count:3}}); - n1.emit("input", {payload:testString3, parts:{id:"X", index:2, count:3}}); - }); - }); - - it('should skip several lines from start if requested', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { a: 9, b: 0, c: "A", d: "B" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should skip several lines from start then use next line as a template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrin:true, skip: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload', { "9": "C", "0": "D", "A": "E", "B": "F" }); - check_parts(msg, 0, 1); - done(); - }); - var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10)+"C,D,E,F"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should skip several lines from start and correct parts', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", skip: 2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c===0) { - msg.should.have.property('payload', { a: 9, b: 0, c: "A", d: "B" }); - check_parts(msg, 0, 2); - c = c+1; - } - else { - msg.should.have.property('payload', { a: "C", b: "D", c: "E", d: "F" }); - check_parts(msg, 1, 2); - done(); - } - }); - var testString = "1,2,3,4"+String.fromCharCode(10)+"5,6,7,8"+String.fromCharCode(10)+"9,0,A,B"+String.fromCharCode(10)+"C,D,E,F"+String.fromCharCode(10); - n1.emit("input", {payload:testString}); - }); - }); - - it('should be able to skip and then use the first of multiple parts as a template if parts are present', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrin:true, skip:2, wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 0) { - msg.should.have.property('payload', { w: 1, x: 2, y: 3, z: 4 }); - msg.should.have.property('columns', 'w,x,y,z'); - check_parts(msg, 0, 2); - c += 1; - } - else { - msg.should.have.property('payload', { w: 5, x: 6, y: 7, z: 8 }); - msg.should.have.property('columns', 'w,x,y,z'); - check_parts(msg, 1, 2); - done(); - } - }); - var testStringA = "foo\n"; - var testStringB = "bar\n"; - var testString1 = "w,x,y,z\n"; - var testString2 = "1,2,3,4\n"; - var testString3 = "5,6,7,8\n"; - n1.emit("input", {payload:testStringA, parts:{id:"X", index:0, count:5}}); - n1.emit("input", {payload:testStringB, parts:{id:"X", index:1, count:5}}); - n1.emit("input", {payload:testString1, parts:{id:"X", index:2, count:5}}); - n1.emit("input", {payload:testString2, parts:{id:"X", index:3, count:5}}); - n1.emit("input", {payload:testString3, parts:{id:"X", index:4, count:5}}); - }); - }); - - }); - - describe('json object to csv', function() { - - it('should convert a simple object back to a csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '4,foo,true,,0\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { e:0, d:1, b:"foo", c:true, a:4 }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert a simple object back to a csv with no template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:" ", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '1,foo,"ba""r","di,ng"\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert a simple object back to a tsv using a tab as a separator', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", sep:"\t", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '1\tfoo\t"ba""r"\tdi,ng\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should handle a template with spaces in the property names', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b o,c p,,e", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '4,foo,true,,0\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { e:0, d:1, "b o":"foo", "c p":true, a:4 }; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,d,c,b", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '4,1,2,3\n1,4,3,2\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv and add a header', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrout:"all", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'a,b,c,d\n4,3,2,1\n1,2,3,4\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv without a template', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '1,3,2,4\n4,2,3,1\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of objects to a multi-line csv without a template and with a header', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"all", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'd,b,c,a\n1,3,2,4\n4,2,3,1\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert a simple array back to a csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', ',0,1,foo,"ba""r","di,ng"\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = ["",0,1,"foo",'ba"r','di,ng']; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should convert an array of arrays back to a multi-line csv', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '0,1,2,3,4\n4,3,2,1,0\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [[0,1,2,3,4],[4,3,2,1,0]]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should be able to include column names as first row', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", hdrout:true, ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'a,b,c,d\r\n4,3,2,1\r\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 }]; - n1.emit("input", {payload:testJson}); - }); - }); - - it('should be able to include column names as first row, and missing properties', function(done) { - var flow = [ { id:"n1", type:"csv", hdrout:true, ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'col1,col2,col3,col4\r\nH1,H2,H3,H4\r\nA,B,,\r\nA,,C,\r\nA,,,D\r\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = [{"col1":"H1","col2":"H2","col3":"H3","col4":"H4"},{"col1":"A","col2":"B"},{"col1":"A","col3":"C"},{"col1":"A","col4":"D"}]; - n1.emit("input", {payload:testJson}); - }); - }); - - - it('should be able to pass in column names', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - count += 1; - try { - if (count === 1) { - msg.should.have.property('payload', 'a,,b,a\r\n4,,3,4\r\n'); - } - if (count === 3) { - msg.should.have.property('payload', '4,,3,4\r\n'); - done() - } - } - catch(e) { done(e); } - }); - var testJson = [{ d: 1, b: 3, c: 2, a: 4 }]; - n1.emit("input", {payload:testJson, columns:"a,,b,a", parts:{index:0}}); - n1.emit("input", {payload:testJson, parts:{index:1}}); - n1.emit("input", {payload:testJson, parts:{index:2}}); - }); - }); - - it('should be able to pass in column names - with payload as an array', function(done) { - var flow = [ { id:"n1", type:"csv", hdrout:"once", ret:"\r\n", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', 'a,,b,a\r\n4,,3,4\r\n4,,3,4\r\n4,,3,4\r\n'); - done() - } - catch(e) { done(e); } - }); - var testJson = { d: 1, b: 3, c: 2, a: 4 }; - n1.emit("input", {payload:[testJson,testJson,testJson], columns:"a,,b,a"}); - }); - }); - - it('should handle quotes and sub-properties', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload', '{},"text,with,commas","This ""is"" a banana","{""sub"":""object""}"\n'); - done(); - } - catch(e) { done(e); } - }); - var testJson = { d: {sub:"object"}, b: "text,with,commas", c: 'This "is" a banana', a: {sub2:undefined} }; - n1.emit("input", {payload:testJson}); - }); - }); - - }); - - it('should just pass through if no payload provided', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', { a: 4, b: 3, c: 2, d: 1 }); - msg.should.not.have.property('payload'); - - done(); - } - catch(e) { done(e); } - }); - var testJson = { d: 1, b: 3, c: 2, a: 4 }; - n1.emit("input", {topic:testJson}); - }); - }); - - it('should warn if provided a number or boolean', function(done) { - var flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[["n2"]] }, - {id:"n2", type:"helper"} ]; - helper.load(csvNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "csv"; - }); - logEvents.should.have.length(2); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith('csv.errors.csv_js'); - logEvents[1][0].should.have.a.property('msg'); - logEvents[1][0].msg.toString().should.startWith('csv.errors.csv_js'); - done(); - } catch(err) { - done(err); - } - },150); - n1.emit("input", {payload:1}); - n1.emit("input", {payload:true}); - }); - }); - - it('should call done when message processing is completed', function(done) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[[]]}, - { id:"c1", type:"complete", scope: ["n1"], uncaught:false, wires:[["h1"]]}, - { id:"h1", type:"helper", wires:[[]]} ]; - helper.load([csvNode,completeNode], flow, function() { - const n1 = helper.getNode("n1"); - const h1 = helper.getNode("h1"); - h1.on("input", function(msg) { - try { - msg.should.have.a.property('payload', "1,2,3,4"); - done(); - } catch (e) { - done(e); - } - }); - n1.receive({payload:"1,2,3,4"}); - }); - }); - - it('should call done when input causes an error', function(done) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const flow = [ { id:"n1", type:"csv", temp:"a,b,c,d", wires:[[]]}, - { id:"c1", type:"complete", scope: ["n1"], uncaught:false, wires:[["h1"]]}, - { id:"h1", type:"helper", wires:[[]]} ]; - helper.load([csvNode,completeNode], flow, function() { - const n1 = helper.getNode("n1"); - const h1 = helper.getNode("h1"); - h1.on("input", function(msg) { - try { - msg.should.have.a.property('payload', 1); - done(); - } catch (e) { - done(e); - } - }); - n1.receive({payload:1}); // neither object nor string - }); - }); -}); diff --git a/test/nodes/core/parsers/70-HTML_spec.js b/test/nodes/core/parsers/70-HTML_spec.js deleted file mode 100644 index da7246e64..000000000 --- a/test/nodes/core/parsers/70-HTML_spec.js +++ /dev/null @@ -1,494 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var path = require("path"); -var fs = require('fs-extra'); - -var htmlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-HTML.js"); -var helper = require("node-red-node-test-helper"); - -describe('HTML node', function() { - - var resourcesDir = __dirname+ path.sep + ".." + path.sep + ".." + path.sep + ".." + path.sep + "resources" + path.sep; - var file = path.join(resourcesDir, "70-HTML-test-file.html"); - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - beforeEach(function() { - fs.existsSync(file).should.be.true(); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"htmlNode1", type:"html", name: "htmlNode" }]; - helper.load(htmlNode, flow, function() { - var htmlNode1 = helper.getNode("htmlNode1"); - htmlNode1.should.have.property('name', 'htmlNode'); - done(); - }); - }); - - it('should retrieve header contents if asked to by msg.select', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - should.equal(msg.payload, 'This is a test page for node 70-HTML'); - done(); - } catch(err) { - done(err) - } - }); - // include 'body' in the select to verify we're in document mode - // for the parser. See https://github.com/node-red/node-red/issues/3079 - n1.receive({payload:data,topic:"bar",select:"body h1"}); - }); - }); - }); - - it('should retrieve header contents if asked to by msg.select - alternative in property', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.foo[0].should.equal('This is a test page for node 70-HTML'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({foo:data,topic:"bar",select:"h1"}); - }); - }); - }); - - it('should retrieve header contents if asked to by msg.select - alternative in and out properties', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",property:"foo",outproperty:"bar",tag:"h1",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.bar[0].should.equal('This is a test page for node 70-HTML'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({foo:data,topic:"bar"}); - }); - }); - }); - - it('should emit an empty array if no matching elements', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('payload'); - msg.payload.should.be.empty; - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic:"bar",select:"h4"}); - }); - }); - }); - - it('should retrieve paragraph contents when specified', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"text",tag:"p"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - should.equal(msg.payload, 'There\'s nothing to read here.'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve list contents as an array of html as default', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ol"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.payload[0].indexOf("
  • Blue
  • ").should.be.above(-1); - msg.payload[0].indexOf("
  • Red
  • ").should.be.above(-1); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve list contents as an array of text', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ol",ret:"text"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.payload[0].indexOf("Blue").should.be.above(-1); - msg.payload[0].indexOf("Red").should.be.above(-1); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should fix up a unclosed tag', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"span"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - should.equal(msg.payload, ''); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve an attribute from a tag', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"attr",tag:"span img"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload[0].should.have.property('src','foo.png'); - msg.should.have.property('topic', 'bar'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should log on error', function(done) { - fs.readFile(file,function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"p"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - try { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:null,topic: "bar"}); - setTimeout(function() { - try { - helper.log().called.should.be.true(); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "html"; - }); - logEvents.should.have.length(1); - // Each logEvent is the array of args passed to the function. - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - }); - - it('should pass through if payload empty', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - } catch(err) { - done(err) - } - }); - n1.receive({topic: "bar"}); - }); - }); - }); - - describe('multiple messages', function(){ - var cnt = 0; - var parts_id = undefined; - - afterEach(function() { - cnt.should.be.exactly(2); - cnt = 0; - parts_id = undefined; - }); - - function check_parts(msg, index, count) { - msg.should.have.property('parts'); - msg.parts.should.have.property('id'); - if(parts_id === undefined) { - parts_id = msg.parts.id; - } - else { - msg.parts.should.have.property('id', parts_id); - } - msg.parts.should.have.property('index', index); - msg.parts.should.have.property('count', count); - msg.parts.should.have.property('type', 'string'); - msg.parts.should.have.property('ch', ''); - } - - it('should retrieve list contents as html as default with output as multiple msgs', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - cnt++; - msg.should.have.property('topic', 'bar'); - check_parts(msg, cnt -1, 2); - if (cnt !== 1 && cnt !== 2) { - return false; - } - if (cnt === 1) { - msg.payload.indexOf("
  • Apple
  • ").should.be.above(-1); - msg.payload.indexOf("
  • Pear
  • ").should.be.above(-1); - } else if (cnt === 2) { - msg.payload.indexOf("
  • Potato
  • ").should.be.above(-1); - msg.payload.indexOf("
  • Parsnip
  • ").should.be.above(-1); - done(); - } - } catch(err) { - done(err) - } - - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - - it('should retrieve list contents as html as default with output as multiple msgs - alternative property', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],tag:"ul",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - cnt++; - msg.should.have.property('topic', 'bar'); - check_parts(msg, cnt -1, 2); - if (cnt !== 1 && cnt !== 2) { - return false; - } - if (cnt === 1) { - msg.foo.indexOf("
  • Apple
  • ").should.be.above(-1); - msg.foo.indexOf("
  • Pear
  • ").should.be.above(-1); - } else if (cnt === 2) { - msg.foo.indexOf("
  • Potato
  • ").should.be.above(-1); - msg.foo.indexOf("
  • Parsnip
  • ").should.be.above(-1); - done(); - } - } catch(err) { - done(err) - } - }); - n1.receive({foo:data, topic:"bar"}); - }); - }); - }); - - it('should retrieve list contents as text with output as multiple msgs ', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",ret:"text",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - cnt++; - msg.should.have.property('topic', 'bar'); - check_parts(msg, cnt -1, 2); - if (cnt !== 1 && cnt !== 2) { - return false; - } - if (cnt === 1) { - msg.payload.indexOf("Apple").should.be.above(-1); - msg.payload.indexOf("Pear").should.be.above(-1); - } else if (cnt === 2) { - msg.payload.indexOf("Potato").should.be.above(-1); - msg.payload.indexOf("Parsnip").should.be.above(-1); - done(); - } - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should retrieve an attribute from a tag', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],ret:"attr",tag:"span img",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.have.property('src','foo.png'); - msg.should.have.property('topic', 'bar'); - check_parts(msg, 0, 1); - cnt = 2; // frig the answer as only one img tag - done(); - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - it('should not reuse message', function(done) { - fs.readFile(file, 'utf8', function(err, data) { - var flow = [{id:"n1",type:"html",wires:[["n2"]],tag:"ul",ret:"text",as:"multi"}, - {id:"n2", type:"helper"}]; - - helper.load(htmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var prev_msg = undefined; - n2.on("input", function(msg) { - try { - cnt++; - if (prev_msg == undefined) { - prev_msg = msg; - } - else { - msg.should.not.equal(prev_msg); - } - if (cnt == 2) { - done(); - } - } catch(err) { - done(err) - } - }); - n1.receive({payload:data,topic: "bar"}); - }); - }); - }); - - }); - -}); diff --git a/test/nodes/core/parsers/70-JSON_spec.js b/test/nodes/core/parsers/70-JSON_spec.js deleted file mode 100644 index ad469bbd5..000000000 --- a/test/nodes/core/parsers/70-JSON_spec.js +++ /dev/null @@ -1,546 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var jsonNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-JSON.js"); -var helper = require("node-red-node-test-helper"); - -describe('JSON node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should convert a valid json string to a javascript object', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees[0].should.have.property('firstName', 'John'); - msg.payload.employees[0].should.have.property('lastName', 'Smith'); - done(); - }); - var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; - jn1.receive({payload:jsonString,topic: "bar"}); - }); - }); - - it('should convert a javascript object to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); - done(); - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a array to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '[1,2,3]'); - done(); - }); - var obj = [1,2,3]; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a boolean to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, 'true'); - done(); - }); - var obj = true; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a json string to a boolean', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, true); - done(); - }); - var obj = "true"; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a number to a json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '2019'); - done(); - }); - var obj = 2019; - jn1.receive({payload:obj}); - }); - }); - - it('should convert a json string to a number', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, 1962); - done(); - }); - var obj = '1962'; - jn1.receive({payload:obj}); - }); - }); - - it('should log an error if asked to parse an invalid json string', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn1.receive({payload:'foo',topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("Unexpected token o"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },20); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if asked to parse something thats not json or js', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.eql('json.errors.dropped-object'); - done(); - } catch(err) { - done(err); - } - },50); - jn1.receive({payload:Buffer.from("a")}); - }); - }); - - it('should pass straight through if no payload set', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - }); - jn1.receive({topic: "bar"}); - }); - }); - - it('should ensure the result is a json string', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var count = 0; - jn2.on("input", function(msg) { - try { - should.equal(msg.payload, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); - count++; - if (count === 2) { - done(); - } - } catch(err) { - done(err); - } - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - jn1.receive({payload:obj,topic: "bar"}); - jn1.receive({payload:JSON.stringify(obj),topic: "bar"}); - }); - }); - - it('should ensure the result is a JS Object', function(done) { - var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var count = 0; - jn2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees[0].should.have.property('firstName', 'John'); - msg.payload.employees[0].should.have.property('lastName', 'Smith'); - count++; - if (count === 2) { - done(); - } - } catch(err) { - done(err); - } - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - jn1.receive({payload:obj,topic: "bar"}); - jn1.receive({payload:JSON.stringify(obj),topic: "bar"}); - }); - }); - - it('should handle any msg property - receive existing string', function(done) { - var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - try { - msg.should.have.property('topic', 'bar'); - msg.should.have.property('one'); - msg.one.should.have.property('two'); - msg.one.two.should.have.property('employees'); - msg.one.two.employees[0].should.have.property('firstName', 'John'); - msg.one.two.employees[0].should.have.property('lastName', 'Smith'); - done(); - } catch(err) { - done(err); - } - }); - var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; - jn1.receive({payload:"",one:{two:jsonString},topic: "bar"}); - - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - }); - }); - - it('should handle any msg property - receive existing obj', function(done) { - var flow = [{id:"jn1",type:"json",property:"one.two",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - try { - should.equal(msg.one.two, '{"employees":[{"firstName":"John","lastName":"Smith"}]}'); - done(); - } catch(err) { - done(err); - } - }); - var jsonString = '{"employees":[{"firstName":"John", "lastName":"Smith"}]}'; - jn1.receive({payload:"",one:{two:JSON.parse(jsonString)},topic: "bar"}); - - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - }); - }); - - it('should pass an object if provided a valid JSON string and schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload.number, 3); - should.equal(msg.payload.string, "allo"); - done(); - }); - var jsonString = '{"number": 3, "string": "allo"}'; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonString, schema:schema}); - }); - }); - - it('should pass an object if provided a valid object and schema and action is object', function(done) { - var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload.number, 3); - should.equal(msg.payload.string, "allo"); - done(); - }); - var obj = {"number": 3, "string": "allo"}; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:obj, schema:schema}); - }); - }); - - it('should pass a string if provided a valid object and schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '{"number":3,"string":"allo"}'); - done(); - }); - var obj = {"number": 3, "string": "allo"}; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:obj, schema:schema}); - }); - }); - - it('should pass a string if provided a valid JSON string and schema and action is string', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.payload, '{"number":3,"string":"allo"}'); - done(); - }); - var jsonString = '{"number":3,"string":"allo"}'; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonString, schema:schema}); - }); - }); - - it('should log an error if passed an invalid object and valid schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var obj = {"number": "foo", "string": 3}; - jn1.receive({payload:obj, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed an invalid object and valid schema and action is object', function(done) { - var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var obj = {"number": "foo", "string": 3}; - jn1.receive({payload:obj, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed an invalid JSON string and valid schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var jsonString = '{"number":"Hello","string":3}'; - jn1.receive({payload:jsonString, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed an invalid JSON string and valid schema and action is string', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - var jsonString = '{"number":"Hello","string":3}'; - jn1.receive({payload:jsonString, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("json.errors.schema-error"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if passed a valid object and invalid schema', function(done) { - var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - try { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - var schema = "garbage"; - var obj = {"number": "foo", "string": 3}; - jn1.receive({payload:obj, schema:schema}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "json"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.equal("json.errors.schema-error-compile"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('msg.schema property should be deleted before sending to next node (string input)', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.schema, undefined); - done(); - }); - var jsonString = '{"number":3,"string":"allo"}'; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonString, schema:schema}); - }); - }); - - it('msg.schema property should be deleted before sending to next node (object input)', function(done) { - var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]}, - {id:"jn2", type:"helper"}]; - helper.load(jsonNode, flow, function() { - var jn1 = helper.getNode("jn1"); - var jn2 = helper.getNode("jn2"); - jn2.on("input", function(msg) { - should.equal(msg.schema, undefined); - done(); - }); - var jsonObject = {"number":3,"string":"allo"}; - var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}}; - jn1.receive({payload:jsonObject, schema:schema}); - }); - }); -}); diff --git a/test/nodes/core/parsers/70-XML_spec.js b/test/nodes/core/parsers/70-XML_spec.js deleted file mode 100644 index e8b281855..000000000 --- a/test/nodes/core/parsers/70-XML_spec.js +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var xmlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-XML.js"); -var helper = require("node-red-node-test-helper"); - -describe('XML node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"xmlNode1", type:"xml", name: "xmlNode" }]; - helper.load(xmlNode, flow, function() { - var xmlNode1 = helper.getNode("xmlNode1"); - xmlNode1.should.have.property('name', 'xmlNode'); - done(); - }); - }); - - it('should convert a valid xml string to a javascript object', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees.should.have.property('firstName'); - should.equal(msg.payload.employees.firstName[0], 'John'); - msg.payload.employees.should.have.property('lastName'); - should.equal(msg.payload.employees.lastName[0], 'Smith'); - done(); - }); - var string = 'JohnSmith'; - n1.receive({payload:string,topic: "bar"}); - }); - }); - - it('should convert a valid xml string to a javascript object - alternative property', function(done) { - var flow = [{id:"n1",type:"xml",property:"foo",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.foo.should.have.property('employees'); - msg.foo.employees.should.have.property('firstName'); - should.equal(msg.foo.employees.firstName[0], 'John'); - msg.foo.employees.should.have.property('lastName'); - should.equal(msg.foo.employees.lastName[0], 'Smith'); - done(); - }); - var string = 'JohnSmith'; - n1.receive({foo:string,topic: "bar"}); - }); - }); - - it('should convert a valid xml string to a javascript object with options', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees.should.have.property('firstName'); - should.equal(msg.payload.employees.firstName[0], 'John'); - msg.payload.employees.should.have.property('lastName'); - should.equal(msg.payload.employees.lastName[0], 'Smith'); - done(); - }); - var string = 'JohnSmith'; - n1.receive({payload:string, topic:"bar", options:{trim:true}}); - }); - }); - - it('should convert a javascript object to an xml string', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - var index = msg.payload.indexOf('JohnSmith'); - index.should.be.above(-1); - done(); - }); - var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }}; - n1.receive({payload:obj,topic: "bar"}); - }); - }); - - it('should convert a javascript object to an xml string with options - alternative property', function(done) { - var flow = [{id:"n1",type:"xml",property:"foo",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - var index = msg.foo.indexOf('\n John\n Smith\n'); - index.should.be.above(-1); - done(); - }); - var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }}; - n1.receive({foo:obj, topic:"bar", options:{headless:true}}); - }); - }); - - it('should log an error if asked to parse an invalid xml string', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:'',topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "xml"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Error: Attribute without value"); - done(); - } catch(err) { - done(err); - } - },200); - }); - }); - - it('should log an error if asked to parse something thats not xml or js', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.receive({payload:1,topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "xml"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg',"xml.errors.xml_js"); - done(); - } catch(err) { - done(err); - } - },200); - }); - }); - - it('should just pass through if payload is missing', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, - {id:"n2", type:"helper"}]; - helper.load(xmlNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - }); - n1.receive({topic: "bar"}); - }); - }); - -}); diff --git a/test/nodes/core/parsers/70-YAML_spec.js b/test/nodes/core/parsers/70-YAML_spec.js deleted file mode 100644 index 3441e0946..000000000 --- a/test/nodes/core/parsers/70-YAML_spec.js +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var yamlNode = require("nr-test-utils").require("@node-red/nodes/core/parsers/70-YAML.js"); -var helper = require("node-red-node-test-helper"); - -describe('YAML node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"yamlNode1", type:"yaml", name: "yamlNode" }]; - helper.load(yamlNode, flow, function() { - var yamlNode1 = helper.getNode("yamlNode1"); - yamlNode1.should.have.property('name', 'yamlNode'); - done(); - }); - }); - - it('should convert a valid yaml string to a javascript object', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.payload.should.have.property('employees'); - msg.payload.employees[0].should.have.property('firstName', 'John'); - msg.payload.employees[0].should.have.property('lastName', 'Smith'); - done(); - }); - var yamlString = "employees:\n - firstName: John\n lastName: Smith\n"; - yn1.receive({payload:yamlString,topic: "bar"}); - }); - }); - - it('should convert a valid yaml string to a javascript object - using another property', function(done) { - var flow = [{id:"yn1",type:"yaml",property:"foo",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.foo.should.have.property('employees'); - msg.foo.employees[0].should.have.property('firstName', 'John'); - msg.foo.employees[0].should.have.property('lastName', 'Smith'); - done(); - }); - var yamlString = "employees:\n - firstName: John\n lastName: Smith\n"; - yn1.receive({foo:yamlString,topic: "bar"}); - }); - }); - - it('should convert a javascript object to a yaml string', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - should.equal(msg.payload, "employees:\n - firstName: John\n lastName: Smith\n"); - done(); - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - yn1.receive({payload:obj}); - }); - }); - - it('should convert a javascript object to a yaml string - using another property', function(done) { - var flow = [{id:"yn1",type:"yaml",property:"foo",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - should.equal(msg.foo, "employees:\n - firstName: John\n lastName: Smith\n"); - done(); - }); - var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; - yn1.receive({foo:obj}); - }); - }); - - it('should convert an array to a yaml string', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - should.equal(msg.payload, "- 1\n- 2\n- 3\n"); - done(); - }); - var obj = [1,2,3]; - yn1.receive({payload:obj}); - }); - }); - - it('should log an error if asked to parse an invalid yaml string', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - try { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn1.receive({payload:'employees:\n-firstName: John\n- lastName: Smith\n',topic: "bar"}); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "yaml"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.should.startWith("end of the stream"); - logEvents[0][0].should.have.a.property('level',helper.log().ERROR); - done(); - } catch(err) { done(err) } - },50); - } catch(err) { - done(err); - } - }); - }); - - it('should log an error if asked to parse something thats not yaml or js', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "yaml"; - }); - logEvents.should.have.length(3); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.eql('yaml.errors.dropped'); - logEvents[1][0].should.have.a.property('msg'); - logEvents[1][0].msg.toString().should.eql('yaml.errors.dropped'); - logEvents[2][0].should.have.a.property('msg'); - logEvents[2][0].msg.toString().should.eql('yaml.errors.dropped-object'); - done(); - } catch(err) { - done(err); - } - },150); - yn1.receive({payload:true}); - yn1.receive({payload:1}); - yn1.receive({payload:Buffer.from("a")}); - }); - }); - - it('should pass straight through if no payload set', function(done) { - var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, - {id:"yn2", type:"helper"}]; - helper.load(yamlNode, flow, function() { - var yn1 = helper.getNode("yn1"); - var yn2 = helper.getNode("yn2"); - yn2.on("input", function(msg) { - msg.should.have.property('topic', 'bar'); - msg.should.not.have.property('payload'); - done(); - }); - yn1.receive({topic: "bar"}); - }); - }); - -}); diff --git a/test/nodes/core/sequence/17-split_spec.js b/test/nodes/core/sequence/17-split_spec.js deleted file mode 100644 index 370e0cda4..000000000 --- a/test/nodes/core/sequence/17-split_spec.js +++ /dev/null @@ -1,1919 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var splitNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/17-split.js"); -var joinNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/17-split.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); - -var TimeoutForErrorCase = 20; - -describe('SPLIT node', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should be loaded', function(done) { - var flow = [{id:"splitNode1", type:"split", name:"splitNode" }]; - helper.load(splitNode, flow, function() { - var splitNode1 = helper.getNode("splitNode1"); - splitNode1.should.have.property('name', 'splitNode'); - done(); - }); - }); - - it('should split an array into multiple messages', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("type","array"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.should.equal(1); } - if (msg.parts.index === 1) { msg.payload.should.equal(2); } - if (msg.parts.index === 2) { msg.payload.should.equal(3); } - if (msg.parts.index === 3) { msg.payload.should.equal(4); done(); } - }); - sn1.receive({payload:[1,2,3,4]}); - }); - }); - - it('should split an array into multiple messages of a specified size', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], arraySplt:3, arraySpltType:"len"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",2); - msg.parts.should.have.property("type","array"); - msg.parts.should.have.property("index"); - msg.payload.should.be.an.Array(); - if (msg.parts.index === 0) { msg.payload.length.should.equal(3); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - sn1.receive({payload:[1,2,3,4]}); - }); - }); - - it('should split an object into pieces', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - var count = 0; - sn2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("type","object"); - msg.parts.should.have.property("key"); - msg.parts.should.have.property("count"); - msg.parts.should.have.property("index"); - msg.topic.should.equal("foo"); - if (msg.parts.index === 0) { msg.payload.should.equal(1); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); } - if (msg.parts.index === 2) { msg.payload.should.equal(true); done(); } - }); - sn1.receive({topic:"foo",payload:{a:1,b:"2",c:true}}); - }); - }); - - it('should split an object into pieces and overwrite their topics', function(done) { - var flow = [{id:"sn1", type:"split", addname:"topic", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - var count = 0; - sn2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("type","object"); - msg.parts.should.have.property("key"); - msg.parts.should.have.property("count"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.should.equal(1); msg.topic.should.equal("a"); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); msg.topic.should.equal("b"); } - if (msg.parts.index === 2) { msg.payload.should.equal(true); msg.topic.should.equal("c"); done(); } - }); - sn1.receive({topic:"foo",payload:{a:1,b:"2",c:true}}); - }); - }); - - it('should split a string into new-lines', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]]}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("type","string"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.should.equal("Da"); } - if (msg.parts.index === 1) { msg.payload.should.equal("ve"); } - if (msg.parts.index === 2) { msg.payload.should.equal(" "); } - if (msg.parts.index === 3) { msg.payload.should.equal("CJ"); done(); } - }); - sn1.receive({payload:"Da\nve\n \nCJ"}); - }); - }); - - it('should split a string on a specified char', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"\n"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",3); - msg.parts.should.have.property("ch","\n"); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","string"); - if (msg.parts.index === 0) { msg.payload.should.equal("1"); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); } - if (msg.parts.index === 2) { msg.payload.should.equal("3"); done(); } - }); - sn1.receive({payload:"1\n2\n3"}); - }); - }); - - it('should split a string into lengths', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"2", spltType:"len"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("ch",""); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","string"); - if (msg.parts.index === 0) { msg.payload.should.equal("12"); } - if (msg.parts.index === 1) { msg.payload.should.equal("34"); } - if (msg.parts.index === 2) { msg.payload.should.equal("56"); } - if (msg.parts.index === 3) { msg.payload.should.equal("78"); done(); } - }); - sn1.receive({payload:"12345678"}); - }); - }); - - it('should split a string on a specified char in stream mode', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"\n", stream:true}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("ch","\n"); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","string"); - if (msg.parts.index === 0) { msg.payload.should.equal("1"); } - if (msg.parts.index === 1) { msg.payload.should.equal("2"); } - if (msg.parts.index === 2) { msg.payload.should.equal("3"); } - if (msg.parts.index === 3) { msg.payload.should.equal("4"); } - if (msg.parts.index === 4) { msg.payload.should.equal("5"); } - if (msg.parts.index === 5) { msg.payload.should.equal("6"); done(); } - }); - sn1.receive({payload:"1\n2\n3\n"}); - sn1.receive({payload:"4\n5\n6\n"}); - }); - }); - - it('should split a buffer into lengths', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"2", spltType:"len"}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - try { - //console.log(msg); - msg.should.have.property("parts"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.parts.should.have.property("count",4); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","buffer"); - if (msg.parts.index === 0) { msg.payload.toString().should.equal("12"); } - if (msg.parts.index === 1) { msg.payload.toString().should.equal("34"); } - if (msg.parts.index === 2) { msg.payload.toString().should.equal("56"); } - if (msg.parts.index === 3) { msg.payload.toString().should.equal("78"); done(); } - } catch(err) { - done(err); - } - }); - var b = Buffer.from("12345678"); - sn1.receive({payload:b}); - }); - }); - - it('should split a buffer on another buffer (streaming)', function(done) { - var flow = [{id:"sn1", type:"split", wires:[["sn2"]], splt:"[52]", spltType:"bin", stream:true}, - {id:"sn2", type:"helper"}]; - helper.load(splitNode, flow, function() { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function(msg) { - try { - msg.should.have.property("parts"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.parts.should.have.property("index"); - msg.parts.should.have.property("type","buffer"); - if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); } - if (msg.parts.index === 1) { msg.payload.toString().should.equal("123"); } - if (msg.parts.index === 2) { msg.payload.toString().should.equal("123"); done(); } - } catch(err) { - done(err); - } - }); - var b1 = Buffer.from("123412"); - var b2 = Buffer.from("341234"); - sn1.receive({payload:b1}); - sn1.receive({payload:b2}); - }); - }); - - it('should handle invalid spltType (not an array)', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "1", spltType: "bin", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - sn2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should handle invalid splt length', function (done) { - var flow = [{ id: "sn1", type: "split", splt: 0, spltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - sn2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should handle invalid array splt length', function (done) { - var flow = [{ id: "sn1", type: "split", arraySplt: 0, arraySpltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - sn2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should ceil count value when msg.payload type is string', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count", 2); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.length.should.equal(2); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should handle spltBufferString value of undefined', function (done) { - var flow = [{ id: "sn1", type: "split", wires: [["sn2"]], splt: "[52]", spltType: "bin" }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - try { - msg.should.have.property("parts"); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); done(); } - } catch (err) { - done(err); - } - }); - sn1.receive({ payload: "123" }); - }); - }); - - it('should ceil count value when msg.payload type is Buffer', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "len", wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count", 2); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.length.should.equal(2); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - var b = Buffer.from("123"); - sn1.receive({ payload: b }); - }); - }); - - it('should set msg.parts.ch when node.spltType is str', function (done) { - var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "str", stream: false, wires: [["sn2"]] }, - { id: "sn2", type: "helper" }]; - helper.load(splitNode, flow, function () { - var sn1 = helper.getNode("sn1"); - var sn2 = helper.getNode("sn2"); - sn2.on("input", function (msg) { - msg.should.have.property("parts"); - msg.parts.should.have.property("count", 2); - msg.parts.should.have.property("index"); - if (msg.parts.index === 0) { msg.payload.length.should.equal(2); } - if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); } - }); - var b = Buffer.from("123"); - sn1.receive({ payload: b }); - }); - }); - -}); - -describe('JOIN node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function(){ - return Context.clean({allNodes:{}}); - }).then(function(){ - return Context.close(); - }).then(function(){ - RED.settings.nodeMessageBufferMaxLength = 0; - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"joinNode1", type:"join", name:"joinNode" }]; - helper.load(joinNode, flow, function() { - var joinNode1 = helper.getNode("joinNode1"); - joinNode1.should.have.property('name', 'joinNode'); - joinNode1.should.have.property('count', 0); - joinNode1.should.have.property('timer', 0); - joinNode1.should.have.property('build', 'array'); - done(); - }); - }); - - it('should join bits of string back together automatically', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:",", build:"string", mode:"auto"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("A,B,C,D"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:4}}); - n1.receive({payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:4}}); - n1.receive({payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:4}}); - n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}}); - }); - }); - it('should join bits of string back together automatically with a buffer joiner', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:"[44]", joinerType:"bin", build:"string", mode:"auto"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("A,B,C,D"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:4}}); - n1.receive({payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:4}}); - n1.receive({payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:4}}); - n1.receive({payload:"D", parts:{id:1, type:"string", ch:",", index:3, count:4}}); - }); - }); - - it('should join bits of buffer back together automatically', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], joiner:",", build:"buffer", mode:"auto"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.toString().should.equal("A-B-C-D"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:Buffer.from("A"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:0, count:4}}); - n1.receive({payload:Buffer.from("B"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:1, count:4}}); - n1.receive({payload:Buffer.from("C"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:2, count:4}}); - n1.receive({payload:Buffer.from("D"), parts:{id:1, type:"buffer", ch:Buffer.from("-"), index:3, count:4}}); - }); - }); - - it('should join things into an array after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(true); - //msg.payload[2].a.should.equal(1); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:1}); - n1.receive({payload:true}); - n1.receive({payload:{a:1}}); - }); - }); - it('should join things into an array ignoring msg.parts.index in manual mode', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(true); - //msg.payload[2].a.should.equal(1); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:1, parts: {index: 3}}); - n1.receive({payload:true, parts: {index: 0}}); - n1.receive({payload:{a:1}, parts: {index: 9}}); - }); - }); - - it('should join things into an array after a count with a buffer join set', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joinerType:"bin", joiner:"" ,mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(true); - //msg.payload[2].a.should.equal(1); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:1}); - n1.receive({payload:true}); - n1.receive({payload:{a:1}}); - }); - }); - - it('should join strings into a buffer after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:2, build:"buffer", joinerType:"bin", joiner:"", mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.length.should.equal(10); - msg.payload.toString().should.equal("helloworld"); - done(); - } - catch(e) {done(e);} - }); - n1.receive({payload:"hello"}); - n1.receive({payload:"world"}); - }); - }); - - it('should join things into an object after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:5, build:"object", mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b","2"); - msg.payload.should.have.property("c",true); - msg.payload.should.have.property("d"); - msg.payload.d.should.have.property("e",7); - // msg.payload.should.have.property("g"); - // msg.payload.g.should.have.property("f",6); - done(); - } - catch(e) { done(e)} - }); - n1.receive({payload:1, topic:"a"}); - n1.receive({payload:"2", topic:"b"}); - n1.receive({payload:true, topic:"c"}); - n1.receive({payload:{e:5}, topic:"d"}); - n1.receive({payload:{e:7}, topic:"d"}); - n1.receive({payload:{f:6}, topic:"g"}); - }); - }); - - it('should merge objects', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:5, build:"merged", mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - msg.payload.should.have.property("e",5); - done(); - } - catch(e) { done(e)} - }); - n1.receive({payload:{a:9}, topic:"f"}); - n1.receive({payload:{a:1}, topic:"a"}); - n1.receive({payload:{b:9}, topic:"b"}); - n1.receive({payload:{b:2}, topic:"b"}); - n1.receive({payload:{c:3}, topic:"c"}); - n1.receive({payload:{d:4}, topic:"d"}); - n1.receive({payload:{e:5}, topic:"e"}); - }); - }); - - it('should merge full msg objects', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], count:6, build:"merged", mode:"custom", propertyType:"full", property:""}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.have.property("payload",7); - msg.payload.should.have.property("aha",'c'); - msg.payload.should.have.property("bar",'b'); - msg.payload.should.have.property("bingo",'e'); - msg.payload.should.have.property("foo",'d'); - msg.payload.should.have.property("topic",'a'); - done(); - } - catch(e) { done(e)} - }); - n1.receive({payload:1, topic:"f"}); - n1.receive({payload:2, topic:"a"}); - n1.receive({payload:3, foo:"b"}); - n1.receive({payload:4, bar:"b"}); - n1.receive({payload:5, aha:"c"}); - n1.receive({payload:6, foo:"d"}); - n1.receive({payload:7, bingo:"e"}); - }); - }); - - it('should accumulate a merged object', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:3}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 3) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",3); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",1); - done(); - } - catch(e) { done(e) } - } - c += 1; - }); - n1.receive({payload:{a:1}, topic:"a"}); - n1.receive({payload:{b:2}, topic:"b"}); - n1.receive({payload:{c:3}, topic:"c"}); - n1.receive({payload:{a:3}, topic:"d"}); - n1.receive({payload:{b:2}, topic:"e"}); - n1.receive({payload:{c:1}, topic:"f"}); - }); - }); - - it('should be able to reset an accumulation', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom", count:3}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - if (c === 1) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - } - catch(e) { done(e) } - } - if (c === 2) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("e",2); - msg.payload.should.have.property("f",1); - } - catch(e) { done(e) } - } - if (c === 3) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("g",2); - msg.payload.should.have.property("h",1); - msg.payload.should.have.property("i",3); - done(); - } - catch(e) { done(e) } - } - c += 1; - }); - n1.receive({payload:{a:1}, topic:"a"}); - n1.receive({payload:{b:2}, topic:"b"}); - n1.receive({payload:{c:3}, topic:"c"}); - n1.receive({payload:{d:4}, topic:"d", complete:true}); - n1.receive({payload:{e:2}, topic:"e"}); - n1.receive({payload:{f:1}, topic:"f", complete:true}); - n1.receive({payload:{g:2}, topic:"g"}); - n1.receive({payload:{h:1}, topic:"h"}); - n1.receive({reset:true}); - n1.receive({payload:{g:2}, topic:"g"}); - n1.receive({payload:{h:1}, topic:"h"}); - n1.receive({payload:{i:3}, topic:"i"}); - }); - }); - - it('should accumulate a key/value object', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"object", accumulate:true, mode:"custom", topic:"bar", key:"foo", count:4}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - //msg.should.have.property("topic","bar"); - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - done(); - } - catch(e) { done(e) } - }); - n1.receive({payload:2, foo:"b"}); - n1.receive({payload:3, foo:"c"}); - n1.receive({reset:true}); - n1.receive({payload:1, foo:"a"}); - n1.receive({payload:2, foo:"b"}); - n1.receive({payload:3, foo:"c"}); - n1.receive({payload:4, foo:"d"}); - }); - }); - - it('should join strings with a specifed character after a timeout', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:0.05, count:"", joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("a,b,c"); - done(); - } - catch(e) { done(e) } - }); - n1.receive({payload:"a"}); - n1.receive({payload:"b"}); - n1.receive({payload:"c"}); - }); - }); - - it('should allow the timeout to be restarted', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:0.5, count:"", joiner:",",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("a,b,c"); - const timeTaken = (Date.now() - start)/1000; - // Node times out after 0.5s. - // It receives a restartTimeout after 0.4s. - // So time taken to timeout should be approx 0.9 - timeTaken.should.be.approximately(0.9,0.15); - done(); - } - catch(e) { done(e) } - }); - var start = Date.now(); - n1.receive({payload:"a"}); - setTimeout(function() { - n1.receive({payload:"b", restartTimeout: true}); - n1.receive({payload:"c"}); - },400); - }); - }); - it('should join strings with a specifed character and complete when told to', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"string", timeout:5, count:0, joiner:"\n",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal("Hello\nNodeRED\nWorld\n"); - done(); - } - catch(e) { done(e) } - }); - n1.receive({payload:"Hello"}); - n1.receive({payload:"NodeRED"}); - n1.receive({payload:"World"}); - n1.receive({payload:'', complete:true}); - }); - }); - - it('should join complete message objects into an array after a count', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"array", timeout:0, count:3, propertyType:"full",mode:"custom"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.payload.should.be.an.Array(); - msg.payload[0].should.be.an.Object(); - msg.payload[0].should.have.property("payload","a"); - msg.payload[1].should.be.an.Object(); - msg.payload[1].should.have.property("payload","b"); - msg.payload[2].should.be.an.Object(); - msg.payload[2].should.have.property("payload","c"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:"a"}); - n1.receive({payload:"b"}); - n1.receive({payload:"c"}); - }); - }); - - it('should join split things back into an array', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(2); - msg.payload[2].should.equal(3); - msg.payload[3].should.equal(4); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:111}}); - n1.receive({payload:2, parts:{index:1, count:4, id:111}}); - n1.receive({payload:4, parts:{index:3, count:4, id:111}}); - n1.receive({payload:1, parts:{index:0, count:4, id:111}}); - }); - }); - - it('should join split things back into an object', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.have.property("a",1); - msg.payload.should.have.property("b",2); - msg.payload.should.have.property("c",3); - msg.payload.should.have.property("d",4); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222, key:"c", type:"object"}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222, key:"b", type:"object"}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222, key:"d", type:"object"}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222, key:"a", type:"object"}}); - }); - }); - - it('should join split things, send when told complete', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:0.250}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - (msg.payload[0] === undefined).should.be.true(); - msg.payload[1].should.equal(2); - msg.payload[2].should.equal(3); - msg.payload[3].should.equal(4); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:444} }); - n1.receive({payload:2, parts:{index:1, count:4, id:444} }); - n1.receive({payload:4, parts:{index:3, count:4, id:444}, complete:true}); - }); - }); - - it('should manually join things into an array, send when told complete', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:1, mode:"custom", build:"array"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload.length.should.equal(3); - msg.payload[0].should.equal(1); - msg.payload[1].should.equal(2); - msg.payload[2].should.equal(3); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:1, topic:"A"}); - n1.receive({payload:2, topic:"B"}); - n1.receive({payload:3, topic:"C"}); - n1.receive({complete:true}); - }); - }); - - - it('should manually join things into an object, send when told complete', function(done) { - var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:1, mode:"custom", build:"object"}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Object(); - Object.keys(msg.payload).length.should.equal(3); - msg.payload.A.should.equal(1); - msg.payload.B.should.equal(2); - msg.payload.C.should.equal(3); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:1, topic:"A"}); - n1.receive({payload:2, topic:"B"}); - n1.receive({payload:3, topic:"C"}); - n1.receive({complete:true}); - }); - }); - - it('should join split strings back into a word', function(done) { - var flow = [{id:"n1", type:"join", mode:"auto", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.String(); - msg.payload.should.equal("abcd"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:"a", parts:{type:'string',index:0, count:4, ch:"", id:555}}); - n1.receive({payload:"d", parts:{type:'string',index:3, count:4, ch:"", id:555}}); - n1.receive({payload:"c", parts:{type:'string',index:2, count:4, ch:"", id:555}}); - n1.receive({payload:"b", parts:{type:'string',index:1, count:4, ch:"", id:555}}); - }); - }); - - it('should allow chained split-split-join-join sequences', function(done) { - var flow = [{id:"s1", type:"split",wires:[["s2"]]}, - {id:"s2", type:"split",wires:[["j1"]]}, - {id:"j1", type:"join", mode:"auto", wires:[["j2"]]}, - {id:"j2", type:"join", mode:"auto", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var s1 = helper.getNode("s1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.eql([[1,2,3],"a\nb\nc",[7,8,9]]); - done(); - } - catch(e) { done(e); } - }); - s1.receive({payload:[[1,2,3],"a\nb\nc",[7,8,9]]}); - }); - }); - - it('should reduce messages', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(10); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages - count only in last part', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(10); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, id:222}}); - n1.receive({payload:2, parts:{index:1, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4,id:222}}); - n1.receive({payload:1, parts:{index:0, id:222}}); - }); - }); - - function checkInitTypes(itype, ival, rval, initializer, checker, done) { - var flow = [{id:"n1", z:"f0", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A", - reduceInit:ival, - reduceInitType:itype, - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - if (!initializer) { - initializer = (node, cb) => { - cb(); - }; - } - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - initializer(n1, function () { - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - checker(msg.payload, rval); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - } - - function checkInitTypesSimple(itype, val, done) { - checkInitTypes(itype, val, val, undefined, should.equal, done); - } - - function checkInitTypesComplex(itype, ival, rval, done) { - checkInitTypes(itype, ival, rval, undefined, should.deepEqual, done); - } - - it('should reduce messages with init types (str)', function(done) { - checkInitTypesSimple('str', "xyz", done); - }); - - it('should reduce messages with init types (num)', function(done) { - checkInitTypesSimple('num', 10, done); - }); - - it('should reduce messages with init types (bool)', function(done) { - checkInitTypesSimple('bool', true, done); - }); - - it('should reduce messages with init types (json)', function(done) { - var ival = '{"x":"vx", "y":"vy", "z":"vz"}'; - var rval = JSON.parse(ival); - checkInitTypesComplex('json', ival, rval, done); - }); - - it('should reduce messages with init types (bin)', function(done) { - var ival = "[1,2,3]"; - var rval = Buffer.from(JSON.parse(ival)); - checkInitTypesComplex('bin', ival, rval, done); - }); - - it('should reduce messages with init types (JSONata)', function(done) { - var ival = "1+2+3"; - var rval = 6; - checkInitTypesComplex('jsonata', ival, rval, done); - }); - - it('should reduce messages with init types (env)', function(done) { - function init(node, cb) { - process.env.NR_XYZ = "nr_xyz"; - cb(); - } - function fin(err) { - delete process.env.NR_XYZ; - done(err); - } - checkInitTypes('env', "NR_XYZ", "nr_xyz", init, should.equal, fin); - }); - - it('should reduce messages with init types (flow.name)', function(done) { - function init(node, cb) { - var context = node.context(); - context.flow.set("foo", "bar"); - cb(); - } - checkInitTypes('flow', "foo", "bar", init, should.equal, done); - }); - - it('should reduce messages with init types (global.name)', function(done) { - function init(node, cb) { - var context = node.context(); - context.global.set("foo", "bar"); - cb(); - } - checkInitTypes('global', "foo", "bar", init, should.equal, done); - }); - - it('should reduce messages using $I', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+$I", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(6); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with fixup', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:"$A/$N", - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(2); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:3, parts:{index:2, count:5, id:222}}); - n1.receive({payload:2, parts:{index:1, count:5, id:222}}); - n1.receive({payload:4, parts:{index:3, count:5, id:222}}); - n1.receive({payload:1, parts:{index:0, count:5, id:222}}); - n1.receive({payload:0, parts:{index:4, count:5, id:222}}); - }); - }); - - it('should reduce messages (left)', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"'(' & $A & '+' & payload & ')'", - reduceInit:"0", - reduceInitType:"str", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.String(); - msg.payload.should.equal("((((0+1)+2)+3)+4)"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:'3', parts:{index:2, count:4, id:222}}); - n1.receive({payload:'2', parts:{index:1, count:4, id:222}}); - n1.receive({payload:'4', parts:{index:3, count:4, id:222}}); - n1.receive({payload:'1', parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages (right)', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:true, - reduceExp:"'(' & $A & '+' & payload & ')'", - reduceInit:"0", - reduceInitType:"str", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.String(); - msg.payload.should.equal("((((0+4)+3)+2)+1)"); - done(); - } - catch(e) { done(e); } - }); - n1.receive({payload:'3', parts:{index:2, count:4, id:222}}); - n1.receive({payload:'2', parts:{index:1, count:4, id:222}}); - n1.receive({payload:'4', parts:{index:3, count:4, id:222}}); - n1.receive({payload:'1', parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with array result', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$append($A,[payload])", - reduceInit:"[]", - reduceInitType:"json", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - var payload = msg.payload; - payload.length.should.equal(2); - if (count == 0) { - payload[0].should.equal(1); - payload[1].should.equal(2); - } - else if (count == 1){ - payload[0].should.equal(3); - payload[1].should.equal(4); - done(); - } - count++; - } - catch(e) { done(e); } - }); - n1.receive({payload:1, parts:{index:0, count:2, id:222}}); - n1.receive({payload:2, parts:{index:1, count:2, id:222}}); - n1.receive({payload:3, parts:{index:2, count:2, id:333}}); - n1.receive({payload:4, parts:{index:3, count:2, id:333}}); - }); - }); - - it('should handle too many pending messages for reduce mode', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+payload", - reduceInit:"0", - reduceInitType:"num", - reduceFixup:undefined, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "join"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "join"); - evt.should.have.property('msg', "join.too-many"); - done(); - }, TimeoutForErrorCase); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with flow context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload*$flowContext(\"two\"))", - reduceInit:"$flowContext(\"one\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$flowContext(\"three\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1*2)+2*2)+3*2)+4*2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().flow.set("one",1); - n1.context().flow.set("two",2); - n1.context().flow.set("three",3); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with global context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload/$globalContext(\"two\"))", - reduceInit:"$globalContext(\"one\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$globalContext(\"three\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1/2)+2/2)+3/2)+4/2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().global.set("one",1); - n1.context().global.set("two",2); - n1.context().global.set("three",3); - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - }); - }); - - it('should reduce messages with persistable flow context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload*$flowContext(\"two\",\"memory\"))", - reduceInit:"$flowContext(\"one\",\"memory\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$flowContext(\"three\",\"memory\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - try { - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1*2)+2*2)+3*2)+4*2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().flow.set(["one","two","three"],[1,2,3],"memory",function(err){ - if(err){ - done(err); - } else{ - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - } - }); - }catch(err) { - done(err); - } - }); - }); - }); - - it('should reduce messages with persistable global context', function(done) { - var flow = [{id:"n1", type:"join", mode:"reduce", - reduceRight:false, - reduceExp:"$A+(payload/$globalContext(\"two\",\"memory\"))", - reduceInit:"$globalContext(\"one\",\"memory\")", - reduceInitType:"jsonata", - reduceFixup:"$A*$globalContext(\"three\",\"memory\")", - wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - helper.load(joinNode, flow, function() { - initContext(function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.equal(((((1+1/2)+2/2)+3/2)+4/2)*3); - done(); - } - catch(e) { done(e); } - }); - n1.context().global.set(["one","two","three"],[1,2,3],"memory",function(err){ - if(err){ - done(err); - } else{ - n1.receive({payload:3, parts:{index:2, count:4, id:222}}); - n1.receive({payload:2, parts:{index:1, count:4, id:222}}); - n1.receive({payload:4, parts:{index:3, count:4, id:222}}); - n1.receive({payload:1, parts:{index:0, count:4, id:222}}); - } - }); - }); - }); - }); - - it('should handle invalid JSONata reduce expression - syntax error"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "invalid expr", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: undefined, - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - - it('should handle invalid JSONata reduce expression - runtime error"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "$uknown()", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: undefined, - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - - it('should handle invalid JSONata fixup expression - syntax err"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "$A", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: "invalid expr", - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - it('should handle invalid JSONata fixup expression - runtime err"', function (done) { - var flow = [{ - id: "n1", type: "join", mode: "reduce", - reduceRight: false, - reduceExp: "$A", - reduceInit: "0", - reduceInitType: "num", - reduceFixup: "$unknown()", - wires: [["n2"]] - }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } }); - }); - }); - - it('should concat payload when group.type is array', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], build: "array", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload.should.be.an.Array(); - msg.payload[0].should.equal("ab"); - msg.payload[1].should.equal("cd"); - msg.payload[2].should.equal("ef"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: "ab", parts: { id: 1, type: "array", ch: ",", index: 0, count: 3, len:2}}); - n1.receive({ payload: "cd", parts: { id: 1, type: "array", ch: ",", index: 1, count: 3, len:2}}); - n1.receive({ payload: "ef", parts: { id: 1, type: "array", ch: ",", index: 2, count: 3, len:2}}); - }); - }); - - it('should concat payload when group.type is buffer and group.joinChar is undefined', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: ",", build: "buffer", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.toString().should.equal("ABC"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: Buffer.from("A"), parts: { id: 1, type: "buffer", index: 0, count: 3 } }); - n1.receive({ payload: Buffer.from("B"), parts: { id: 1, type: "buffer", index: 1, count: 3 } }); - n1.receive({ payload: Buffer.from("C"), parts: { id: 1, type: "buffer", index: 2, count: 3 } }); - }); - }); - - it('should concat payload when group.type is string and group.joinChar is not string', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: ",", build: "buffer", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload.toString().should.equal("A0B0C"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: Buffer.from("A"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 0, count: 3 } }); - n1.receive({ payload: Buffer.from("B"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 1, count: 3 } }); - n1.receive({ payload: Buffer.from("C"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 2, count: 3 } }); - }); - }); - - it('should handle msg.parts property when mode is auto and parts or id are missing', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "string", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - done(new Error("This path does not go through.")); - }); - n1.receive({ payload: "A", parts: { type: "string", ch: ",", index: 0, count: 2 } }); - n1.receive({ payload: "B", parts: { type: "string", ch: ",", index: 1, count: 2 } }); - setTimeout(function () { - done(); - }, TimeoutForErrorCase); - }); - }); - - it('should handle join an array when mode is auto and duplicate indexed parts arrive', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload[0].should.equal("C"); - msg.payload[1].should.equal("D"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: "A", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "B", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "C", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "D", parts: { id:1, type:"array", ch:",", index:1, count:2 } }); - }); - }); - - it('should handle join an array when using msg.parts and duplicate indexed parts arrive and being reset halfway', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - msg.payload[0].should.equal("D"); - msg.payload[1].should.equal("C"); - done(); - } - catch (e) { done(e); } - }); - n1.receive({ payload: "A", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ payload: "B", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - n1.receive({ reset:true }); - n1.receive({ payload: "C", parts: { id:1, type:"array", ch:",", index:1, count:2 } }); - n1.receive({ payload: "D", parts: { id:1, type:"array", ch:",", index:0, count:2 } }); - }); - }); - - describe('messaging API', function() { - function mapiDoneSplitTestHelper(done, splt, spltType, stream, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - { id: "splitNode1", type:"split", splt, spltType, stream, wires: [[]]}, - { id: "completeNode1", type: "complete", scope: ["splitNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["splitNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([splitNode, completeNode, catchNode], flow, function () { - const splitNode1 = helper.getNode("splitNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 2; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { splitNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when message is sent (string)', function (done) { - mapiDoneSplitTestHelper(done, 2, "len", false, [ - { msg: { seq: 0, payload: "12345" }, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when message is sent (array)', function (done) { - mapiDoneSplitTestHelper(done, 2, "len", false, [ - { msg: { seq: 0, payload: [0,1,2,3,4] }, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when message is sent (object)', function (done) { - mapiDoneSplitTestHelper(done, 2, "len", false, [ - { msg: { seq: 0, payload: {a:1,b:2}}, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when consolidated message is emitted (string, len)', function (done) { - mapiDoneSplitTestHelper(done, 5, "len", true, [ - { msg: { seq: 0, payload: "12"}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: "34"}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: "5"}, delay: 500, avr: 500, var: 100 } - ]); - }); - it('should call done() when consolidated message is emitted (Buffer, len)', function (done) { - mapiDoneSplitTestHelper(done, 5, "len", true, [ - { msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 } - ]); - }); - it('should call done() when consolidated message is emitted (Buffer, str)', function (done) { - mapiDoneSplitTestHelper(done, "5", "str", true, [ - { msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 } - ]); - }); - it('should call done() when consolidated message is emitted (Buffer, bin)', function (done) { - mapiDoneSplitTestHelper(done, "[53]", "bin", true, [ - { msg: { seq: 0, payload: Buffer.from("12")}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: Buffer.from("34")}, delay: 200, avr: 500, var: 100 }, - { msg: { seq: 2, payload: Buffer.from("5")}, delay: 500, avr: 500, var: 100 } - ]); - }); - - function mapiDoneJoinTestHelper(done, joinNodeSetting, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - { ...joinNodeSetting, id: "joinNode1", type:"join", wires: [[]]}, - { id: "completeNode1", type: "complete", scope: ["joinNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["joinNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([joinNode, completeNode, catchNode], flow, function () { - const joinNode1 = helper.getNode("joinNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 3; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { joinNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when all messages are joined', function (done) { - mapiDoneJoinTestHelper(done, {mode:"auto", timeout:1}, [ - { msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100}, - { msg: {seq:2, payload:"C", parts:{id:1, type:"string", ch:",", index:2, count:3}}, delay:500, avr:500, var:100} - ]); - }); - it('should call done() when the node is reset', function (done) { - mapiDoneJoinTestHelper(done, {mode:"auto", timeout:1}, [ - { msg: {seq:0, payload:"A", parts:{id:1, type:"string", ch:",", index:0, count:3}}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:"B", parts:{id:1, type:"string", ch:",", index:1, count:3}}, delay:200, avr:500, var:100}, - { msg: {seq:2, payload:"dummy", reset: true, parts:{id:1}}, delay:500, avr:500, var:100} - ]); - }); - it('should call done() when timed out', function (done) { - mapiDoneJoinTestHelper(done, {mode:"custom", joiner:",", build:"string", timeout:0.5}, [ - { msg: {seq:0, payload:"A"}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:"B"}, delay:200, avr:500, var:100}, - ]); - }); - it('should call done() when all messages are reduced', function (done) { - mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0", - reduceInitType:"num", reduceFixup:undefined}, [ - { msg: {seq:0, payload:3, parts: {index:2, count:3, id:222}}, delay:0, avr:500, var:100}, - { msg: {seq:1, payload:2, parts: {index:1, count:3, id:222}}, delay:200, avr:500, var:100}, - { msg: {seq:2, payload:4, parts: {index:0, count:3, id:222}}, delay:500, avr:500, var:100} - ]); - }); - it('should call done() regardless of buffer overflow', function (done) { - mapiDoneJoinTestHelper(done, {mode:"reduce", reduceRight:false, reduceExp:"$A+payload", reduceInit:"0", - reduceInitType:"num", reduceFixup:undefined}, [ - { msg: {seq:0, payload:3, parts: {index:2, count:5, id:222}}, delay:0, avr:600, var:100}, - { msg: {seq:1, payload:2, parts: {index:1, count:5, id:222}}, delay:200, avr:600, var:100}, - { msg: {seq:2, payload:4, parts: {index:0, count:5, id:222}}, delay:400, avr:600, var:100}, - { msg: {seq:3, payload:1, parts: {index:3, count:5, id:222}}, delay:600, avr:600, var:100}, - ]); - }); - }); - - it('should handle msg.parts even if messages are out of order in auto mode if exactly one message has count set', function (done) { - var flow = [{ id: "n1", type: "join", wires: [["n2"]], mode: "auto" }, - { id: "n2", type: "helper" }]; - helper.load(joinNode, flow, function () { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - - n2.on("input", function (msg) { - msg.payload.length.should.be.eql(5); - msg.payload.should.be.eql([0,1,2,3,4]); - done(); - }); - - var msg = {}; - msg.parts = { - id: RED.util.generateId() - }; - for(var elem = 1; elem < 5; ++elem) { - var _msg = RED.util.cloneMessage(msg); - _msg.parts.index = elem; - if(elem == 4) { - _msg.parts.count = 5; - } - _msg.payload = elem; - n1.receive(_msg); - } - - var _msg = RED.util.cloneMessage(msg); - delete _msg.parts.count; - _msg.parts.index = 0; - _msg.payload = 0; - n1.receive(_msg); - }); - - }) - -}); diff --git a/test/nodes/core/sequence/18-sort_spec.js b/test/nodes/core/sequence/18-sort_spec.js deleted file mode 100644 index 955038bfb..000000000 --- a/test/nodes/core/sequence/18-sort_spec.js +++ /dev/null @@ -1,554 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sortNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/18-sort.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); -var Context = require("nr-test-utils").require("@node-red/runtime/lib/nodes/context"); - -describe('SORT node', function() { - - beforeEach(function(done) { - helper.startServer(done); - }); - - function initContext(done) { - Context.init({ - contextStorage: { - memory: { - module: "memory" - } - } - }); - Context.load().then(function () { - done(); - }); - } - - afterEach(function(done) { - helper.unload().then(function(){ - RED.settings.nodeMessageBufferMaxLength = 0; - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, name: "SortNode", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'SortNode'); - done(); - }); - }); - - function check_sort0(flow, target, key, key_type, data_in, data_out, done) { - var sort = flow[0]; - sort.target = target; - sort.targetType = "msg"; - sort.msgKey = key; - sort.msgKeyType = key_type; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property(target); - var data = msg[target]; - data.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - var data0 = data[i]; - var data1 = data_out[i]; - if (typeof data0 === "object") { - data0.should.deepEqual(data1); - } - else { - data0.should.equal(data1); - } - } - done(); - } - catch(e) { - console.log(e); - } - }); - var msg = {}; - msg[target] = data_in; - n1.receive(msg); - }); - } - - function check_sort0A(flow, data_in, data_out, done) { - check_sort0(flow, "payload", "", "elem", data_in, data_out, done); - } - - function check_sort0B(flow, data_in, data_out, done) { - check_sort0(flow, "data", "", "elem", data_in, data_out, done); - } - - function check_sort0C(flow, exp, data_in, data_out, done) { - check_sort0(flow, "data", exp, "jsonata", data_in, data_out, done); - } - - function check_sort1(flow, key, key_type, data_in, data_out, done) { - function equals(v0, v1) { - var k0 = Object.keys(v0); - var k1 = Object.keys(v1); - - if (k0.length === k1.length) { - for (var i = 0; i < k0.length; i++) { - var k = k0[i]; - if (!v1.hasOwnProperty(k) || - (v0[k] !== v1[k])) { - return false; - } - } - return true; - } - return false; - } - function indexOf(a, v) { - for(var i = 0; i < a.length; i++) { - var av = a[i]; - if ((typeof v === 'object') && equals(v, av)) { - return i; - } - else if (v === av) { - return i; - } - } - return -1; - } - var sort = flow[0]; - var prop = (key_type === "msg") ? key : "payload"; - sort.targetType = "seq"; - sort.seqKey = key; - sort.seqKeyType = key_type; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n2.on("input", function(msg) { - msg.should.have.property(prop); - msg.should.have.property("parts"); - msg.parts.should.have.property("count", data_out.length); - var data = msg[prop]; - var index = indexOf(data_out, data); - msg.parts.should.have.property("index", index); - count++; - if (count === data_out.length) { - done(); - } - }); - var len = data_in.length; - for(var i = 0; i < len; i++) { - var parts = { id: "X", index: i, count: len }; - var msg = {parts: parts}; - msg[prop] = data_in[i]; - n1.receive(msg); - } - }); - } - - function check_sort1A(flow, data_in, data_out, done) { - check_sort1(flow, "payload", "msg", data_in, data_out, done); - } - - function check_sort1B(flow, data_in, data_out, done) { - check_sort1(flow, "data", "msg", data_in, data_out, done); - } - - function check_sort1C(flow, exp, data_in, data_out, done) { - check_sort1(flow, exp, "jsonata", data_in, data_out, done); - } - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "1000", "200", "30", "4" ]; - it('should sort payload (elem, not number, ascending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, not number, ascending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (not number, ascending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (not number, ascending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "4", "30", "200", "1000" ]; - it('should sort payload (elem, not number, descending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, not number, descending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (not number, descending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (not number, descending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "4", "30", "200", "1000" ]; - it('should sort payload (elem, number, ascending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, number, ascending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (number, ascending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (number, ascending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "1000", "200", "30", "4" ]; - it('should sort payload (elem, number, descending)', function(done) { - check_sort0A(flow, data_in, data_out, done); - }); - it('should sort msg prop (elem, number, descending)', function(done) { - check_sort0B(flow, data_in, data_out, done); - }); - it('should sort message group/payload (number, descending)', function(done) { - check_sort1A(flow, data_in, data_out, done); - }); - it('should sort message group/prop (number, descending)', function(done) { - check_sort1B(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "C200", "A4", "B30", "D1000" ]; - var data_out = [ "D1000", "C200", "B30", "A4" ]; - it('should sort payload (exp, not number, ascending)', function(done) { - check_sort0C(flow, "$substring($,1)", data_in, data_out, done); - }); - it('should sort message group (exp, not number, ascending)', function(done) { - check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "C200", "A4", "B30", "D1000" ]; - var data_out = [ "A4", "B30", "C200", "D1000" ]; - it('should sort message group (exp, not number, descending)', function(done) { - check_sort0C(flow, "$substring($,1)", data_in, data_out, done); - }); - it('should sort payload (exp, not number, descending)', function(done) { - check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var conv = function(x) { - return x.map(function(v) { return { val:v }; }); - }; - var data_in = conv([ "200", "4", "30", "1000" ]); - var data_out = conv([ "4", "30", "200", "1000" ]); - it('should sort payload of objects', function(done) { - check_sort0C(flow, "val", data_in, data_out, done); - }); - })(); - - it('should sort payload by context (exp, not number, ascending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$flowContext($)", msgKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "second", "third", "first", "fourth" ]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context()["flow"].set("first","3"); - n1.context()["flow"].set("second","1"); - n1.context()["flow"].set("third","2"); - n1.context()["flow"].set("fourth","4"); - n2.on("input", function(msg) { - msg.should.have.property("data"); - var data = msg["data"]; - data.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - data[i].should.equal(data_out[i]); - } - done(); - }); - var msg = {}; - msg["data"] = data_in; - n1.receive(msg); - }); - }); - - it('should sort message group by context (exp, not number, ascending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"seq", seqKey:"$globalContext(payload)", seqKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "second", "fourth", "third", "first" ]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n1.context()["global"].set("first","4"); - n1.context()["global"].set("second","1"); - n1.context()["global"].set("third","3"); - n1.context()["global"].set("fourth","2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("count", data_out.length); - var data = msg["payload"]; - var index = data_out.indexOf(data); - msg.parts.should.have.property("index", index); - count++; - if (count === data_out.length) { - done(); - } - } - catch(e) { - done(e); - } - }); - var len = data_in.length; - for(var i = 0; i < len; i++) { - var parts = { id: "X", index: i, count: len }; - var msg = {parts: parts}; - msg["payload"] = data_in[i]; - n1.receive(msg); - } - }); - }); - - it('should sort payload by persistable context (exp, not number, descending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$globalContext($,\"memory\")", msgKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "fourth", "first", "third", "second" ]; - helper.load(sortNode, flow, function() { - initContext(function(){ - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n1.context()["global"].set(["first","second","third","fourth"],["3","1","2","4"],"memory",function(){ - n2.on("input", function(msg) { - msg.should.have.property("data"); - var data = msg["data"]; - data.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - data[i].should.equal(data_out[i]); - } - done(); - }); - var msg = {}; - msg["data"] = data_in; - n1.receive(msg); - }); - }); - }); - }); - - it('should sort message group by persistable context (exp, not number, descending)', function(done) { - var flow = [{id:"n1", type:"sort", target:"data", targetType:"seq", seqKey:"$flowContext(payload,\"memory\")", seqKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"}, - {id:"n2", type:"helper",z:"flow"}, - {id:"flow", type:"tab"}]; - var data_in = [ "first", "second", "third", "fourth" ]; - var data_out = [ "first", "third", "fourth", "second" ]; - helper.load(sortNode, flow, function() { - initContext(function(){ - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - n1.context()["flow"].set(["first","second","third","fourth"],["4","1","3","2"],"memory",function(){ - n2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.should.have.property("parts"); - msg.parts.should.have.property("count", data_out.length); - var data = msg["payload"]; - var index = data_out.indexOf(data); - msg.parts.should.have.property("index", index); - count++; - if (count === data_out.length) { - done(); - } - }); - var len = data_in.length; - for(var i = 0; i < len; i++) { - var parts = { id: "X", index: i, count: len }; - var msg = {parts: parts}; - msg["payload"] = data_in[i]; - n1.receive(msg); - } - }); - }); - }); - }); - - it('should handle JSONata script error', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"$unknown()", seqKeyType:"jsonata", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "sort"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "sort"); - evt.should.have.property('msg', "sort.invalid-exp"); - done(); - }, 150); - var msg0 = { payload: "A", parts: { id: "X", index: 0, count: 2} }; - var msg1 = { payload: "B", parts: { id: "X", index: 1, count: 2} }; - n1.receive(msg0); - n1.receive(msg1); - }); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "sort"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "sort"); - evt.should.have.property('msg', "sort.too-many"); - done(); - }, 150); - for(var i = 0; i < 4; i++) { - var msg = { payload: "V"+i, - parts: { id: "X", index: i, count: 4} }; - n1.receive(msg); - } - }); - }); - - it('should clear pending messages on close', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var msg = { payload: 0, - parts: { id: "X", index: 0, count: 2} }; - n1.receive(msg); - setTimeout(function() { - n1.close().then(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "sort"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "sort"); - evt.should.have.property('msg', "sort.clear"); - done(); - }); - }, 150); - }); - }); - - describe('messaging API', function() { - function mapiDoneTestHelper(done, targetType, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [ - {id: "sortNode1", type: "sort", order: "ascending", as_num: false, target: "payload", targetType, - seqKey: "payload", seqKeyType: "msg", wires: [[]]}, - { id: "completeNode1", type: "complete", scope: ["sortNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "catchNode1", type: "catch", scope: ["sortNode1"], uncaught: false, wires: [["helperNode1"]] }, - { id: "helperNode1", type: "helper", wires: [[]] }]; - const numMsgs = msgAndTimings.length; - helper.load([sortNode, completeNode, catchNode], flow, function () { - const sortNode1 = helper.getNode("sortNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 2; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.seq].avr, msgAndTimings[msg.seq].var); - c += 1; - if (c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout(function () { sortNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - it('should call done() when message is sent (payload)', function (done) { - mapiDoneTestHelper(done, "msg", [ - { msg: { seq: 0, payload: [1, 3, 2] }, delay: 0, avr: 0, var: 100 }, - ]); - }); - it('should call done() when message is sent (sequence)', function (done) { - mapiDoneTestHelper(done, "seq", [ - { msg: { seq: 0, payload: 3, parts: {id:"A", index: 0, count: 2}}, delay: 0, avr: 500, var: 100 }, - { msg: { seq: 1, payload: 2, parts: {id:"A", index: 1, count: 2}}, delay: 500, avr: 500, var: 100} - ]); - }); - it('should call done() regardless of buffer overflow (same group)', function (done) { - mapiDoneTestHelper(done, "seq", [ - { msg: { seq: 0, payload: 1, parts: {id:"A", index: 0, count: 3}}, delay: 0, avr: 1000, var: 100 }, - { msg: { seq: 1, payload: 3, parts: {id:"A", index: 1, count: 3}}, delay: 500, avr: 1000, var: 100 }, - { msg: { seq: 2, payload: 2, parts: {id:"A", index: 2, count: 3}}, delay: 1000, avr: 1000, var: 100 }, - ]); - }); - it('should call done() regardless of buffer overflow (different group)', function (done) { - mapiDoneTestHelper(done, "seq", [ - { msg: { seq: 0, payload: 1, parts: {id:"A", index: 0, count: 2}}, delay: 0, avr: 1000, var: 100 }, - { msg: { seq: 1, payload: 3, parts: {id:"B", index: 0, count: 2}}, delay: 500, avr: 1200, var: 100 }, - { msg: { seq: 2, payload: 5, parts: {id:"C", index: 0, count: 2}}, delay: 1000, avr: 1500, var: 100 }, - { msg: { seq: 3, payload: 2, parts: {id:"B", index: 1, count: 2}}, delay: 1200, avr: 1200, var: 100 }, - { msg: { seq: 4, payload: 4, parts: {id:"C", index: 1, count: 2}}, delay: 1500, avr: 1500, var: 100 }, - ]); - }); - }); -}); diff --git a/test/nodes/core/sequence/19-batch_spec.js b/test/nodes/core/sequence/19-batch_spec.js deleted file mode 100644 index 2ebcb8d4d..000000000 --- a/test/nodes/core/sequence/19-batch_spec.js +++ /dev/null @@ -1,542 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var batchNode = require("nr-test-utils").require("@node-red/nodes/core/sequence/19-batch.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red.js"); - -describe('BATCH node', function() { - this.timeout(8000); - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - RED.settings.nodeMessageBufferMaxLength = 0; - }); - - it('should be loaded with defaults', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - n1.should.have.property('name', 'BatchNode'); - done(); - }); - }); - - function check_parts(msg, id, idx, count) { - msg.should.have.property("parts"); - var parts = msg.parts; - parts.should.have.property("id", id); - parts.should.have.property("index", idx); - parts.should.have.property("count", count); - } - - function check_data(n1, n2, results, done) { - var id = undefined; - var ix0 = 0; // seq no - var ix1 = 0; // loc. in seq - var seq = undefined; - var msgs = []; - n2.on("input", function(msg) { - try { - for (var i = 0; i < msgs.length; i++) { - msg.should.not.equal(msgs[i]); - } - msgs.push(msg); - if (seq === undefined) { - seq = results[ix0]; - } - var val = seq[ix1]; - msg.should.have.property("payload", val); - if (id === undefined) { - id = msg.parts.id; - } - check_parts(msg, id, ix1, seq.length); - ix1++; - if (ix1 === seq.length) { - ix0++; - ix1 = 0; - seq = undefined; - id = undefined; - if (ix0 === results.length) { - done(); - } - } - } - catch (e) { - done(e); - } - }); - } - - function check_count(flow, results, done) { - try { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - check_data(n1, n2, results, done); - for(var i = 0; i < 6; i++) { - n1.receive({payload: i}); - } - }); - } - catch (e) { - done(e); - } - } - - function delayed_send(receiver, index, count, delay, done) { - if (index < count) { - setTimeout(function() { - receiver.receive({payload: index}); - delayed_send(receiver, index+1, count, delay, done); - }, delay); - } - else if(index === count) { - if (done) { - done(); - } - } - } - - function check_interval(flow, results, delay, done) { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - check_data(n1, n2, results, done); - delayed_send(n1, 0, 4, delay); - }); - } - - function check_concat(flow, results, inputs, done) { - try { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - check_data(n1, n2, results, done); - for(var data of inputs) { - var msg = { - topic: data[0], - payload: data[1], - parts: { - id: data[0], - index: data[2], - count: data[3] - } - }; - n1.receive(msg); - } - }); - } - catch (e) { - done(e); - } - } - - describe('mode: count', function() { - - it('should create seq. with count', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 2, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1], - [2, 3], - [4, 5] - ]; - check_count(flow, results, done); - }); - - it('should create seq. with count and overlap', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 3, overlap: 2, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1, 2], - [1, 2, 3], - [2, 3, 4], - [3, 4, 5] - ]; - check_count(flow, results, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 5, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "batch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "batch"); - evt.should.have.property('msg', "batch.too-many"); - done(); - }, 150); - for(var i = 0; i < 3; i++) { - n1.receive({payload: i}); - } - }); - }); - - it('should handle reset', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 2, overlap: 0, interval: 0, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var results = [ - [0, 1], - [4, 5] - ]; - check_data(n1, n2, results, done); - n1.receive({payload:0}); - n1.receive({payload:1}); - n1.receive({payload:2}); - n1.receive({payload:3, reset: true}); - n1.receive({payload:4}); - n1.receive({payload:5}); - }); - }); - }); - - describe('mode: interval', function() { - it('should create seq. with interval', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1], - [2, 3] - ]; - check_interval(flow, results, 450, done); - }); - - it('should create seq. with interval (in float)', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 0.5, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [0, 1], - [2, 3] - ]; - check_interval(flow, results, 225, done); - }); - - it('should create seq. with interval & not send empty seq', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - // 1300, 2600, 3900, 5200, - [0], [1], [2], [3] - ]; - check_interval(flow, results, 1300, done); - }); - - it('should create seq. with interval & send empty seq', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: true, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - // 1300, 2600, 3900, 5200, - [null], [0], [1], [2], [null], [3] - ]; - check_interval(flow, results, 1300, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "batch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "batch"); - evt.should.have.property('msg', "batch.too-many"); - done(); - }, 150); - for(var i = 0; i < 3; i++) { - n1.receive({payload: i}); - } - }); - }); - - it('should handle reset', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "interval", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var results = [ - [0, 1], - [4, 5] - ]; - check_data(n1, n2, results, done); - delayed_send(n1, 0, 3, 400, function () { - setTimeout(function () { - n1.receive({payload: "3", reset: true}); - delayed_send(n1, 4, 7, 400); - }, 10); - }); - }); - }); - - }); - - describe('mode: concat', function() { - it('should concat two seq. (series)', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [2, 3, 0, 1] - ]; - var inputs = [ - ["TB", 0, 0, 2], - ["TB", 1, 1, 2], - ["TA", 2, 0, 2], - ["TA", 3, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should concat two seq. (mixed)', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [2, 3, 0, 1] - ]; - var inputs = [ - ["TA", 2, 0, 2], - ["TB", 0, 0, 2], - ["TA", 3, 1, 2], - ["TB", 1, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should concat three seq.', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}, {topic: "TC"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [2, 3, 0, 1, 4] - ]; - var inputs = [ - ["TC", 4, 0, 1], - ["TB", 0, 0, 2], - ["TB", 1, 1, 2], - ["TA", 2, 0, 2], - ["TA", 3, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should concat same seq.', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TA"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = [ - [9, 8, 9, 8] - ]; - var inputs = [ - ["TA", 9, 0, 2], - ["TA", 8, 1, 2] - ]; - check_concat(flow, results, inputs, done); - }); - - it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - RED.settings.nodeMessageBufferMaxLength = 2; - setTimeout(function() { - var logEvents = helper.log().args.filter(function (evt) { - return evt[0].type == "batch"; - }); - var evt = logEvents[0][0]; - evt.should.have.property('id', "n1"); - evt.should.have.property('type', "batch"); - evt.should.have.property('msg', "batch.too-many"); - done(); - }, 150); - var C = 3; - for(var i = 0; i < C; i++) { - var parts_a = {index:i, count:C, id:"A"}; - var parts_b = {index:i, count:C, id:"B"}; - n1.receive({payload: i, topic: "TA", parts:parts_a}); - n1.receive({payload: i, topic: "TB", parts:parts_b}); - } - }); - }); - - it('should handle reset', function(done) { - var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "concat", count: 0, overlap: 0, interval: 1, allowEmptySequence: false, topics: [{topic: "TA"}, {topic: "TB"}], wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - try { - helper.load(batchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var results = [ - [2, 3, 0, 1] - ]; - check_data(n1, n2, results, done); - var inputs0 = [ - ["TB", 0, 0, 2], - ["TA", 1, 0, 2], - ]; - for(var data of inputs0) { - var msg = { - topic: data[0], - payload: data[1], - parts: { - id: data[0], - index: data[2], - count: data[3] - } - }; - n1.receive(msg); - } - n1.receive({payload: undefined, reset: true}); - var inputs1 = [ - ["TB", 0, 0, 2], - ["TB", 1, 1, 2], - ["TA", 2, 0, 2], - ["TA", 3, 1, 2] - ]; - for(var data of inputs1) { - var msg = { - topic: data[0], - payload: data[1], - parts: { - id: data[0], - index: data[2], - count: data[3] - } - }; - n1.receive(msg); - } - }); - } - catch (e) { - done(e); - } - }); - }); - - describe('messaging API', function() { - function mapiDoneTestHelper(done, mode, count, overlap, interval, allowEmptySequence, msgAndTimings) { - const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); - const catchNode = require("nr-test-utils").require("@node-red/nodes/core/common/25-catch.js"); - const flow = [{id:"batchNode1", type:"batch", name: "BatchNode", mode, count, overlap, interval, - allowEmptySequence, topics: [{topic: "TA"}], wires:[[]]}, - {id:"completeNode1",type:"complete",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]}, - {id:"catchNode1", type:"catch",scope: ["batchNode1"],uncaught:false,wires:[["helperNode1"]]}, - {id:"helperNode1",type:"helper", wires:[[]]}]; - const numMsgs = msgAndTimings.length; - helper.load([batchNode, completeNode, catchNode], flow, function () { - const batchNode1 = helper.getNode("batchNode1"); - const helperNode1 = helper.getNode("helperNode1"); - RED.settings.nodeMessageBufferMaxLength = 2; - const t = Date.now(); - let c = 0; - helperNode1.on("input", function (msg) { - msg.should.have.a.property('payload'); - (Date.now() - t).should.be.approximately(msgAndTimings[msg.payload].avr, msgAndTimings[msg.payload].var); - c += 1; - if ( c === numMsgs) { - done(); - } - }); - for (let i = 0; i < numMsgs; i++) { - setTimeout( function() { batchNode1.receive(msgAndTimings[i].msg); }, msgAndTimings[i].delay); - } - }); - } - - it('should call done() when message is sent (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 0, var: 100}, - { msg: {payload: 1}, delay: 0, avr: 0, var: 100} - ]); - }); - it('should call done() when reset (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 200, var: 100}, - { msg: {payload: 1, reset:true}, delay: 200, avr: 200, var: 100} - ]); - }); - it('should call done() regardless of buffer overflow (mode: count)', function(done) { - mapiDoneTestHelper(done, "count", 10, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 500, var: 100}, - { msg: {payload: 1}, delay: 100, avr: 500, var: 100}, - { msg: {payload: 2}, delay: 500, avr: 500, var: 100} - ]); - }); - it('should call done() when message is sent (mode: interval)', function(done) { - mapiDoneTestHelper(done, "interval", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 2000, var: 100}, - { msg: {payload: 1}, delay: 500, avr: 2000, var: 100} - ]); - }); - it('should call done() when reset (mode: interval)', function(done) { - mapiDoneTestHelper(done, "interval", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 200, var: 100}, - { msg: {payload: 1, reset:true}, delay: 200, avr: 200, var: 100} - ]); - }); - it('should call done() regardless of buffer overflow (mode: interval)', function(done) { - mapiDoneTestHelper(done, "interval", 2, 0, 2, false, [ - { msg: {payload: 0}, delay: 0, avr: 500, var: 100}, - { msg: {payload: 1}, delay: 100, avr: 500, var: 100}, - { msg: {payload: 2}, delay: 500, avr: 500, var: 100} - ]); - }); - it('should call done() when message is sent (mode: concat)', function(done) { - mapiDoneTestHelper(done, "concat", 2, 0, 2, false, [ - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 0, count: 2}}, delay: 0, avr: 1000, var: 100}, - { msg: {topic:"TA", payload: 1, parts: {id: "TA", index: 1, count: 2}}, delay: 1000, avr: 1000, var: 100}, - ]); - }); - it('should call done() when reset (mode: concat)', function(done) { - mapiDoneTestHelper(done, "concat", 2, 0, 2, false, [ - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 0, count: 2}}, delay: 0, avr: 1000, var: 100}, - { msg: {payload: 1, reset:true}, delay: 1000, avr: 1000, var: 100}, - ]); - }); - it('should call done() regardless of buffer overflow (mode: concat)', function(done) { - mapiDoneTestHelper(done, "concat", 2, 0, 2, false, [ - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 0, count: 3}}, delay: 0, avr: 1000, var: 100}, - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 1, count: 3}}, delay: 500, avr: 1000, var: 100}, - { msg: {topic:"TA", payload: 0, parts: {id: "TA", index: 2, count: 3}}, delay: 1000, avr: 1000, var: 100} - ]); - }); - }); -}); diff --git a/test/nodes/core/storage/10-file_spec.js b/test/nodes/core/storage/10-file_spec.js deleted file mode 100644 index 99ed23978..000000000 --- a/test/nodes/core/storage/10-file_spec.js +++ /dev/null @@ -1,1722 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var path = require('path'); -var fs = require('fs-extra'); -var os = require('os'); -var sinon = require("sinon"); -var iconv = require("iconv-lite"); -var fileNode = require("nr-test-utils").require("@node-red/nodes/core/storage/10-file.js"); -var helper = require("node-red-node-test-helper"); -var RED = require("nr-test-utils").require("node-red/lib/red"); - -describe('file Nodes', function() { - - function encode(s, enc) { - if (enc === "none") { - return Buffer.from(s); - } - return iconv.encode(s, enc); - } - - function decode(data, enc) { - if (enc === "none") { - return data.toString(); - } - return iconv.decode(data, enc); - } - - describe('file out Node', function() { - - var relativePathToFile = "50-file-test-file.txt"; - var resourcesDir = path.join(__dirname,"..","..","..","resources"); - var fileToTest = path.join(resourcesDir,relativePathToFile); - var wait = 250; - - beforeEach(function(done) { - //fs.writeFileSync(fileToTest, "File message line 1\File message line 2\n"); - helper.startServer(done); - }); - - afterEach(function(done) { - delete RED.settings.fileWorkingDirectory; - fs.removeSync(path.join(resourcesDir,"file-out-node")); - helper.unload().then(function() { - //fs.unlinkSync(fileToTest); - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":true}]; - helper.load(fileNode, flow, function() { - try { - var fileNode1 = helper.getNode("fileNode1"); - fileNode1.should.have.property('name', 'fileNode'); - done(); - } - catch (e) { - done(e); - } - }); - }); - - it('should write to a file', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest); - f.should.have.length(4); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", "test"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"test"}); - }); - }); - - it('should write to a file using RED.settings.fileWorkingDirectory', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":relativePathToFile, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - RED.settings.fileWorkingDirectory = resourcesDir; - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest); - f.should.have.length(4); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", "test"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"test"}); - }); - }); - - - it('should write multi-byte string to a file', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.have.length(2); - f.should.equal("試験"); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", "試験"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"試験"}); - }); - }); - - it('should append to a file and add newline', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - try { - fs.unlinkSync(fileToTest); - } catch(err) { - } - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var count = 0; - var data = ["test2", true, 999, [2]]; - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - data.should.containDeep([msg.payload]); - if (count === 3) { - var f = fs.readFileSync(fileToTest).toString(); - if (os.type() !== "Windows_NT") { - f.should.have.length(19); - f.should.equal("test2\ntrue\n999\n[2]\n"); - } - else { - f.should.have.length(23); - f.should.equal("test2\r\ntrue\r\n999\r\n[2]\r\n"); - } - done(); - } - count++; - } - catch (e) { - done(e); - } - }); - - n1.receive({payload:"test2"}); // string - setTimeout(function() { - n1.receive({payload:true}); // boolean - },30); - setTimeout(function() { - n1.receive({payload:999}); // number - },60); - setTimeout(function() { - n1.receive({payload:[2]}); // object (array) - },90); - }); - }); - - it('should append to a file after it has been deleted ', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - try { - fs.unlinkSync(fileToTest); - } catch(err) { - } - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var data = ["one", "two", "three", "four"]; - var count = 0; - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - data.should.containDeep([msg.payload]); - try { - if (count === 1) { - // Check they got appended as expected - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("onetwo"); - - // Delete the file - fs.unlinkSync(fileToTest); - setTimeout(function() { - // Send two more messages to the file - n1.receive({payload:"three"}); - n1.receive({payload:"four"}); - }, wait); - } - if (count === 3) { - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("threefour"); - fs.unlinkSync(fileToTest); - done(); - } - } catch(err) { - done(err); - } - count++; - } - catch (e) { - done(e); - } - }); - - // Send two messages to the file - n1.receive({payload:"one"}); - n1.receive({payload:"two"}); - }); - }); - - it('should append to a file after it has been recreated ', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - try { - fs.unlinkSync(fileToTest); - } catch(err) { - } - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var data = ["one", "two", "three", "four"]; - var count = 0; - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload"); - data.should.containDeep([msg.payload]); - if (count == 1) { - // Check they got appended as expected - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("onetwo"); - - if (os.type() === "Windows_NT") { - var dummyFile = path.join(resourcesDir,"50-file-test-dummy.txt"); - fs.rename(fileToTest, dummyFile, function() { - recreateTest(n1, dummyFile); - }); - } else { - recreateTest(n1, fileToTest); - } - } - if (count == 3) { - // Check the file was updated - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.equal("threefour"); - fs.unlinkSync(fileToTest); - done(); - } catch(err) { - done(err); - } - } - } catch(err) { - done(err); - } - count++; - }); - - // Send two messages to the file - n1.receive({payload:"one"}); - n1.receive({payload:"two"}); - }); - - function recreateTest(n1, fileToDelete) { - // Delete the file - fs.unlinkSync(fileToDelete); - - // Recreate it - fs.writeFileSync(fileToTest,""); - - // Send two more messages to the file - n1.receive({payload:"three"}); - n1.receive({payload:"four"}); - } - }); - - - it('should use msg.filename if filename not set in node', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - - n2.on("input", function (msg) { - try { - msg.should.have.property("payload", "fine"); - msg.should.have.property("filename", fileToTest); - - var f = fs.readFileSync(fileToTest).toString(); - if (os.type() !== "Windows_NT") { - f.should.have.length(5); - f.should.equal("fine\n"); - } - else { - f.should.have.length(6); - f.should.equal("fine\r\n"); - } - done(); - } - catch (e) { - done(e); - } - }); - - n1.receive({payload:"fine", filename:fileToTest}); - }); - }); - - it('should be able to delete the file', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":"delete", wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - - n2.on("input", function (msg) { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.not.equal("fine"); - //done(); - } - catch(e) { - e.code.should.equal("ENOENT"); - done(); - } - }); - - n1.receive({payload:"fine"}); - }); - }); - - it('should warn if filename not set', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - n1.emit("input", {payload:"nofile"}); - setTimeout(function() { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.not.equal("fine"); - //done(); - } - catch(e) { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.equal("file.errors.nofilename"); - done(); - } - },wait); - }); - }); - - it('ignore a missing payload', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var f = fs.readFileSync(fileToTest).toString(); - f.should.not.equal("fine"); - //done(); - } - catch(e) { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(0); - done(); - } - },wait); - n1.emit("input", {topic:"test"}); - }); - }); - - it('should fail to write to a ro file', function(done) { - // Stub file write so we can make writes fail - var spy = sinon.stub(fs, 'createWriteStream').callsFake(function(arg1,arg2) { - var ws = {}; - ws.on = function(e,d) { throw("Stub error message"); } - ws.write = function(e,d) { } - return ws; - }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Stub error message"); - done(); - } - catch(e) { done(e); } - finally { fs.createWriteStream.restore(); } - },wait); - n1.receive({payload:"test"}); - }); - }); - - it('should fail to append to a ro file', function(done) { - // Stub file write so we can make writes fail - var spy = sinon.stub(fs, 'createWriteStream').callsFake(function(arg1,arg2) { - var ws = {}; - ws.on = function(e,d) { throw("Stub error message"); } - ws.write = function(e,d) { } - return ws; - }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Stub error message"); - done(); - } - catch(e) { done(e); } - finally { fs.createWriteStream.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should cope with failing to delete a file', function(done) { - // Stub file write so we can make writes fail - var spy = sinon.stub(fs, 'unlink').callsFake(function(arg,arg2) { arg2(new Error("Stub error message")); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":"delete"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("file.errors.deletefail"); - done(); - } - catch(e) { done(e); } - finally { fs.unlink.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should fail to create a new directory if not asked to do so (append)', function(done) { - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - //var spy = sinon.stub(fs, 'appendFile').callsFake(function(arg,arg2,arg3,arg4){ arg4(new Error("Stub error message")); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":false}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("file.errors.appendfail"); - done(); - } - catch(e) { done(e); } - //finally { fs.appendFile.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should try to create a new directory if asked to do so (append)', function(done) { - // fs.writeFileSync of afterEach failed on Windows. - if (os.type() === "Windows_NT") { - done(); - return; - } - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - var spy = sinon.stub(fs, "ensureDir").callsFake(function(arg1,arg2,arg3,arg4) { arg2(null); }); - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":false, "createDir":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(0); - done(); - } - catch(e) { done(e); } - finally { fs.ensureDir.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should fail to create a new directory if not asked to do so (overwrite)', function(done) { - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - //var spy = sinon.stub(fs, 'appendFile').callsFake(function(arg,arg2,arg3,arg4){ arg4(new Error("Stub error message")); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":false, "overwriteFile":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("file.errors.writefail"); - done(); - } - catch(e) { done(e); } - //finally { fs.appendFile.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should try to create a new directory if asked to do so (overwrite)', function(done) { - // Stub file write so we can make writes fail - var fileToTest2 = path.join(resourcesDir,"file-out-node","50-file-test-file.txt"); - var spy = sinon.stub(fs, "ensureDir").callsFake(function(arg1,arg2,arg3,arg4) { arg2(null); }); - - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest2, "appendNewline":true, "overwriteFile":true, "createDir":true}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - setTimeout(function() { - try { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file"; - }); - //console.log(logEvents); - logEvents.should.have.length(0); - done(); - } - catch(e) { done(e); } - finally { fs.ensureDir.restore(); } - },wait); - n1.receive({payload:"test2"}); - }); - }); - - it('should write to multiple files', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, "createDir":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - var tmp_path = path.join(resourcesDir, "tmp"); - var len = 1024*1024*10; - var file_count = 5; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var count = 0; - n2.on("input", function(msg) { - try { - count++; - if (count == file_count) { - for(var i = 0; i < file_count; i++) { - var name = path.join(tmp_path, String(i)); - var f = fs.readFileSync(name); - f.should.have.length(len); - f[0].should.have.equal(i); - } - fs.removeSync(tmp_path); - done(); - } - } - catch (e) { - try { - fs.removeSync(tmp_path); - } - catch (e1) { - } - done(e); - } - }); - for(var i = 0; i < file_count; i++) { - var data = Buffer.alloc?Buffer.alloc(len):new Buffer(len); - data.fill(i); - var name = path.join(tmp_path, String(i)); - var msg = {payload:data, filename:name}; - n1.receive(msg); - } - }); - }); - - it('should write to multiple files if node is closed', function(done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, "createDir":true, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - var tmp_path = path.join(resourcesDir, "tmp"); - var len = 1024*1024*10; - var file_count = 5; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - var count = 0; - n2.on("input", function(msg) { - try { - count++; - if (count == file_count) { - for(var i = 0; i < file_count; i++) { - var name = path.join(tmp_path, String(i)); - var f = fs.readFileSync(name); - f.should.have.length(len); - f[0].should.have.equal(i); - } - fs.removeSync(tmp_path); - done(); - } - } - catch (e) { - try { - fs.removeSync(tmp_path); - } - catch (e1) { - } - done(e); - } - }); - for(var i = 0; i < file_count; i++) { - var data = Buffer.alloc?Buffer.alloc(len):new Buffer(len); - data.fill(i); - var name = path.join(tmp_path, String(i)); - var msg = {payload:data, filename:name}; - n1.receive(msg); - } - n1.close(); - }); - }); - - describe('encodings', function() { - - function checkWriteWithEncoding(enc, data, done) { - var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, encoding:enc, wires: [["helperNode1"]]}, - {id:"helperNode1", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileNode1"); - var n2 = helper.getNode("helperNode1"); - n2.on("input", function(msg) { - try { - var f = fs.readFileSync(fileToTest); - f.equals(encode(data, enc)).should.be.true(); - fs.unlinkSync(fileToTest); - msg.should.have.property("payload", data); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:data}); - }); - } - - // default - it('should write to a file with "none" encoding', function(done) { - checkWriteWithEncoding("none", "test", done); - }); - - // Native - it('should write to a file with "utf8" encoding', function(done) { - checkWriteWithEncoding("utf8", "試験", done); - }); - - it('should write to a file with "ucs2" encoding', function(done) { - checkWriteWithEncoding("ucs2", "試験", done); - }); - - it('should write to a file with "utf-16le" encoding', function(done) { - checkWriteWithEncoding("utf-16le", "試験", done); - }); - - it('should write to a file with "binary" encoding', function(done) { - checkWriteWithEncoding("binary", "test", done); - }); - - it('should write to a file with "base64" encoding', function(done) { - checkWriteWithEncoding("base64", "5pel5pys6KqeCg==", done); - }); - - it('should write to a file with "hex" encoding', function(done) { - checkWriteWithEncoding("hex", "deadbeef", done); - }); - - // Unicode - it('should write to a file with "utf-16be" encoding', function(done) { - checkWriteWithEncoding("utf-16be", "試験", done); - }); - - // Japanese - it('should write to a file with "Shift_JIS" encoding', function(done) { - checkWriteWithEncoding("Shift_JIS", "試験", done); - }); - - it('should write to a file with "Windows-31j" encoding', function(done) { - checkWriteWithEncoding("Windows-31j", "試験", done); - }); - - it('should write to a file with "Windows932" encoding', function(done) { - checkWriteWithEncoding("Windows932", "試験", done); - }); - - it('should write to a file with "EUC-JP" encoding', function(done) { - checkWriteWithEncoding("EUC-JP", "試験", done); - }); - - // following encoding tests should be more specific - // Chinese - it('should write to a file with "GB2312" encoding', function(done) { - checkWriteWithEncoding("GB2312", "test", done); - }); - - it('should write to a file with "GBK" encoding', function(done) { - checkWriteWithEncoding("GBK", "test", done); - }); - - it('should write to a file with "GB18030" encoding', function(done) { - checkWriteWithEncoding("GB18030", "test", done); - }); - - it('should write to a file with "Windows936" encoding', function(done) { - checkWriteWithEncoding("Windows936", "test", done); - }); - - it('should write to a file with "EUC-CN" encoding', function(done) { - checkWriteWithEncoding("EUC-CN", "test", done); - }); - - // Korean - it('should write to a file with "KS_C_5601" encoding', function(done) { - checkWriteWithEncoding("KS_C_5601", "test", done); - }); - - it('should write to a file with "Windows949" encoding', function(done) { - checkWriteWithEncoding("Windows949", "test", done); - }); - - it('should write to a file with "EUC-KR" encoding', function(done) { - checkWriteWithEncoding("EUC-KR", "test", done); - }); - - // Taiwan/Hong Kong - it('should write to a file with "Big5" encoding', function(done) { - checkWriteWithEncoding("Big5", "test", done); - }); - - it('should write to a file with "Big5-HKSCS" encoding', function(done) { - checkWriteWithEncoding("Big5-HKSCS", "test", done); - }); - - it('should write to a file with "Windows950" encoding', function(done) { - checkWriteWithEncoding("Windows950", "test", done); - }); - - // Windows - it('should write to a file with "cp874" encoding', function(done) { - checkWriteWithEncoding("cp874", "test", done); - }); - - it('should write to a file with "cp1250" encoding', function(done) { - checkWriteWithEncoding("cp1250", "test", done); - }); - - it('should write to a file with "cp1251" encoding', function(done) { - checkWriteWithEncoding("cp1251", "test", done); - }); - - it('should write to a file with "cp1252" encoding', function(done) { - checkWriteWithEncoding("cp1252", "test", done); - }); - - it('should write to a file with "cp1253" encoding', function(done) { - checkWriteWithEncoding("cp1253", "test", done); - }); - - it('should write to a file with "cp1254" encoding', function(done) { - checkWriteWithEncoding("cp1254", "test", done); - }); - - it('should write to a file with "cp1255" encoding', function(done) { - checkWriteWithEncoding("cp1255", "test", done); - }); - - it('should write to a file with "cp1256" encoding', function(done) { - checkWriteWithEncoding("cp1256", "test", done); - }); - - it('should write to a file with "cp1257" encoding', function(done) { - checkWriteWithEncoding("cp1257", "test", done); - }); - - it('should write to a file with "cp1258" encoding', function(done) { - checkWriteWithEncoding("cp1258", "test", done); - }); - - // IBM - it('should write to a file with "cp437" encoding', function(done) { - checkWriteWithEncoding("cp437", "test", done); - }); - - it('should write to a file with "cp737" encoding', function(done) { - checkWriteWithEncoding("cp737", "test", done); - }); - - it('should write to a file with "cp775" encoding', function(done) { - checkWriteWithEncoding("cp775", "test", done); - }); - - it('should write to a file with "cp808" encoding', function(done) { - checkWriteWithEncoding("cp808", "test", done); - }); - - it('should write to a file with "cp850" encoding', function(done) { - checkWriteWithEncoding("cp850", "test", done); - }); - - it('should write to a file with "cp852" encoding', function(done) { - checkWriteWithEncoding("cp852", "test", done); - }); - - it('should write to a file with "cp855" encoding', function(done) { - checkWriteWithEncoding("cp855", "test", done); - }); - - it('should write to a file with "cp856" encoding', function(done) { - checkWriteWithEncoding("cp856", "test", done); - }); - - it('should write to a file with "cp857" encoding', function(done) { - checkWriteWithEncoding("cp857", "test", done); - }); - - it('should write to a file with "cp858" encoding', function(done) { - checkWriteWithEncoding("cp858", "test", done); - }); - - it('should write to a file with "cp860" encoding', function(done) { - checkWriteWithEncoding("cp860", "test", done); - }); - - it('should write to a file with "cp861" encoding', function(done) { - checkWriteWithEncoding("cp861", "test", done); - }); - - it('should write to a file with "cp866" encoding', function(done) { - checkWriteWithEncoding("cp866", "test", done); - }); - - it('should write to a file with "cp869" encoding', function(done) { - checkWriteWithEncoding("cp869", "test", done); - }); - - it('should write to a file with "cp922" encoding', function(done) { - checkWriteWithEncoding("cp922", "test", done); - }); - - it('should write to a file with "cp1046" encoding', function(done) { - checkWriteWithEncoding("cp1046", "test", done); - }); - - it('should write to a file with "cp1124" encoding', function(done) { - checkWriteWithEncoding("cp1124", "test", done); - }); - - it('should write to a file with "cp1125" encoding', function(done) { - checkWriteWithEncoding("cp1125", "test", done); - }); - - it('should write to a file with "cp1129" encoding', function(done) { - checkWriteWithEncoding("cp1129", "test", done); - }); - - it('should write to a file with "cp1133" encoding', function(done) { - checkWriteWithEncoding("cp1133", "test", done); - }); - - it('should write to a file with "cp1161" encoding', function(done) { - checkWriteWithEncoding("cp1161", "test", done); - }); - - it('should write to a file with "cp1162" encoding', function(done) { - checkWriteWithEncoding("cp1162", "test", done); - }); - - it('should write to a file with "cp1163" encoding', function(done) { - checkWriteWithEncoding("cp1163", "test", done); - }); - - // Mac - it('should write to a file with "maccroatian" encoding', function(done) { - checkWriteWithEncoding("maccroatian", "test", done); - }); - - it('should write to a file with "maccyrillic" encoding', function(done) { - checkWriteWithEncoding("maccyrillic", "test", done); - }); - - it('should write to a file with "macgreek" encoding', function(done) { - checkWriteWithEncoding("macgreek", "test", done); - }); - - it('should write to a file with "maciceland" encoding', function(done) { - checkWriteWithEncoding("maciceland", "test", done); - }); - - it('should write to a file with "macroman" encoding', function(done) { - checkWriteWithEncoding("macroman", "test", done); - }); - - it('should write to a file with "macromania" encoding', function(done) { - checkWriteWithEncoding("macromania", "test", done); - }); - - it('should write to a file with "macthai" encoding', function(done) { - checkWriteWithEncoding("macthai", "test", done); - }); - - it('should write to a file with "macturkish" encoding', function(done) { - checkWriteWithEncoding("macturkish", "test", done); - }); - - it('should write to a file with "macukraine" encoding', function(done) { - checkWriteWithEncoding("macukraine", "test", done); - }); - - it('should write to a file with "maccenteuro" encoding', function(done) { - checkWriteWithEncoding("maccenteuro", "test", done); - }); - - it('should write to a file with "macintosh" encoding', function(done) { - checkWriteWithEncoding("macintosh", "test", done); - }); - - // KOI8 - it('should write to a file with "koi8-r" encoding', function(done) { - checkWriteWithEncoding("koi8-r", "test", done); - }); - - it('should write to a file with "koi8-u" encoding', function(done) { - checkWriteWithEncoding("koi8-u", "test", done); - }); - - it('should write to a file with "koi8-ru" encoding', function(done) { - checkWriteWithEncoding("koi8-ru", "test", done); - }); - - it('should write to a file with "koi8-t" encoding', function(done) { - checkWriteWithEncoding("koi8-t", "test", done); - }); - - // Misc - it('should write to a file with "armscii8" encoding', function(done) { - checkWriteWithEncoding("armscii8", "test", done); - }); - - it('should write to a file with "rk1048" encoding', function(done) { - checkWriteWithEncoding("rk1048", "test", done); - }); - - it('should write to a file with "tcvn" encoding', function(done) { - checkWriteWithEncoding("tcvn", "test", done); - }); - - it('should write to a file with "georgianacademy" encoding', function(done) { - checkWriteWithEncoding("georgianacademy", "test", done); - }); - - it('should write to a file with "georgianps" encoding', function(done) { - checkWriteWithEncoding("georgianps", "test", done); - }); - - it('should write to a file with "pt154" encoding', function(done) { - checkWriteWithEncoding("pt154", "test", done); - }); - - it('should write to a file with "viscii" encoding', function(done) { - checkWriteWithEncoding("viscii", "test", done); - }); - - it('should write to a file with "iso646cn" encoding', function(done) { - checkWriteWithEncoding("iso646cn", "test", done); - }); - - it('should write to a file with "iso646jp" encoding', function(done) { - checkWriteWithEncoding("iso646jp", "test", done); - }); - - it('should write to a file with "hproman8" encoding', function(done) { - checkWriteWithEncoding("hproman8", "test", done); - }); - - it('should write to a file with "tis620" encoding', function(done) { - checkWriteWithEncoding("tis620", "test", done); - }); - - }); - }); - - - describe('file in Node', function() { - - var relativePathToFile = "50-file-test-file.txt"; - var resourcesDir = path.join(__dirname,"..","..","..","resources"); - var fileToTest = path.join(resourcesDir,relativePathToFile); - var fileToTest2 = "\t"+path.join(resourcesDir,relativePathToFile)+"\r\n"; - var wait = 150; - - beforeEach(function(done) { - fs.writeFileSync(fileToTest, "File message line 1\nFile message line 2\n"); - helper.startServer(done); - }); - - afterEach(function(done) { - delete RED.settings.fileWorkingDirectory; - helper.unload().then(function() { - fs.unlinkSync(fileToTest); - helper.stopServer(done); - }); - }); - - it('should be loaded', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - n1.should.have.property('name', 'fileInNode'); - done(); - }); - }); - - it('should read in a file and output a buffer', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name:"fileInNode", "filename":fileToTest, "format":"", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.should.have.length(40); - msg.payload.toString().should.equal('File message line 1\nFile message line 2\n'); - done(); - }); - n1.receive({payload:""}); - }); - }); - - it('should read in a file and output a utf8 string', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.have.length(40) - msg.payload.should.equal("File message line 1\nFile message line 2\n"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - - it('should read in a file using fileWorkingDirectory to set cwd', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":relativePathToFile, "format":"utf8", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - RED.settings.fileWorkingDirectory = resourcesDir; - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.have.length(40) - msg.payload.should.equal("File message line 1\nFile message line 2\n"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - - it('should read in a file ending in cr and output a utf8 string', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest2, "format":"utf8", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.have.length(40) - msg.payload.should.equal("File message line 1\nFile message line 2\n"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - it('should read in a file and output split lines with parts', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.should.have.property('topic'); - msg.should.not.have.property('foo'); - msg.should.not.have.property('bar'); - msg.payload.should.be.a.String(); - msg.should.have.property('parts'); - msg.parts.should.have.property('index',c); - msg.parts.should.have.property('type','string'); - msg.parts.should.have.property('ch','\n'); - if (c === 0) { - msg.payload.should.equal("File message line 1"); - } - if (c === 1) { - msg.payload.should.equal("File message line 2"); - } - if (c === 2) { - msg.payload.should.equal(""); - done(); - } - c++; - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"",topic:"A",foo:"bar",bar:"foo"}); - }); - }); - - it('should read in a file with empty line and output split lines with parts', function(done) { - var data = ["-", "", "-", ""]; - var line = data.join("\n"); - fs.writeFileSync(fileToTest, line); - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.equal(data[c]); - msg.should.have.property('parts'); - var parts = msg.parts; - parts.should.have.property('index',c); - parts.should.have.property('type','string'); - parts.should.have.property('ch','\n'); - c++; - if (c === data.length) { - parts.should.have.property('count', data.length); - done(); - } - else { - parts.should.not.have.property('count'); - } - } - catch(e) { - done(e); - } - }); - n1.receive({payload:""}); - }); - }); - - it('should read in a file and output split lines with parts and extra props', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", allProps:true, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - var c = 0; - n2.on("input", function(msg) { - // console.log(msg) - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.should.have.property('topic'); - msg.should.have.property('foo'); - msg.should.have.property('bar'); - msg.should.have.property('parts'); - msg.parts.should.have.property('index',c); - msg.parts.should.have.property('type','string'); - msg.parts.should.have.property('ch','\n'); - if (c === 0) { - msg.payload.should.equal("File message line 1"); - } - if (c === 1) { - msg.payload.should.equal("File message line 2"); - } - if (c === 2) { - msg.payload.should.equal(""); - done(); - } - c++; - } - catch(e) { - done(e); - } - }); - n1.receive({payload:"",topic:"B",foo:"bar",bar:"foo"}); - }); - }); - - it('should read in a file and output a buffer with parts', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"stream", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property('payload'); - Buffer.isBuffer(msg.payload).should.be.true(); - msg.payload.should.have.length(40); - msg.should.have.property('parts'); - msg.parts.should.have.property('count',1); - msg.parts.should.have.property('type','buffer'); - msg.parts.should.have.property('ch',''); - done(); - }); - n1.receive({payload:""}); - }); - }); - - it('should warn if no filename set', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "format":""}]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - setTimeout(function() { - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file in"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.equal("file.errors.nofilename"); - done(); - },wait); - n1.receive({}); - }); - }); - - it('should handle a file read error', function(done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":"badfile", "format":"", wires:[["n2"]]}, - {id:"n2", type:"helper"} - ]; - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - - n2.on("input", function(msg) { - try { - msg.should.not.have.property('payload'); - msg.should.have.property("error"); - msg.error.should.have.property("code","ENOENT"); - var logEvents = helper.log().args.filter(function(evt) { - return evt[0].type == "file in"; - }); - logEvents.should.have.length(1); - logEvents[0][0].should.have.a.property('msg'); - logEvents[0][0].msg.toString().should.startWith("Error"); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - }); - - describe('encodings', function() { - - function checkReadWithEncoding(enc, data, done) { - var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8", encoding:enc, wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - - fs.writeFileSync(fileToTest, encode(data, enc)); - helper.load(fileNode, flow, function() { - var n1 = helper.getNode("fileInNode1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property('payload'); - msg.payload.should.be.a.String(); - msg.payload.should.equal(data); - done(); - } catch(err) { - done(err); - } - }); - n1.receive({payload:""}); - }); - } - - // default - it('should read in a file with "none" encoding', function(done) { - checkReadWithEncoding("none", "試験", done); - }); - - // Native - it('should read in a file with "utf8" encoding', function(done) { - checkReadWithEncoding("utf8", "試験", done); - }); - - it('should read in a file with "ucs2" encoding', function(done) { - checkReadWithEncoding("ucs2", "試験", done); - }); - - it('should read in a file with "utf-16le" encoding', function(done) { - checkReadWithEncoding("utf-16le", "試験", done); - }); - - it('should read in a file with "binary" encoding', function(done) { - checkReadWithEncoding("binary", "test", done); - }); - - it('should read in a file with "base64" encoding', function(done) { - checkReadWithEncoding("base64", "5pel5pys6KqeCg==", done); - }); - - it('should read in a file with "hex" encoding', function(done) { - checkReadWithEncoding("hex", "deadbeef", done); - }); - - // Unicode - it('should read in a file with "utf-16be" encoding', function(done) { - checkReadWithEncoding("utf-16be", "試験", done); - }); - - // Japanese - it('should read in a file with "Shift_JIS" encoding', function(done) { - checkReadWithEncoding("Shift_JIS", "試験", done); - }); - - it('should read in a file with "Windows-31j" encoding', function(done) { - checkReadWithEncoding("Windows-31j", "試験", done); - }); - - it('should read in a file with "Windows932" encoding', function(done) { - checkReadWithEncoding("Windows932", "試験", done); - }); - - it('should read in a file with "EUC-JP" encoding', function(done) { - checkReadWithEncoding("EUC-JP", "試験", done); - }); - - // following encoding tests should be more specific - // Chinese - it('should read in a file with "GB2312" encoding', function(done) { - checkReadWithEncoding("GB2312", "test", done); - }); - - it('should read in a file with "GBK" encoding', function(done) { - checkReadWithEncoding("GBK", "test", done); - }); - - it('should read in a file with "GB18030" encoding', function(done) { - checkReadWithEncoding("GB18030", "test", done); - }); - - it('should read in a file with "Windows936" encoding', function(done) { - checkReadWithEncoding("Windows936", "test", done); - }); - - it('should read in a file with "EUC-CN" encoding', function(done) { - checkReadWithEncoding("EUC-CN", "test", done); - }); - - // Korean - it('should read in a file with "KS_C_5601" encoding', function(done) { - checkReadWithEncoding("KS_C_5601", "test", done); - }); - - it('should read in a file with "Windows949" encoding', function(done) { - checkReadWithEncoding("Windows949", "test", done); - }); - - it('should read in a file with "EUC-KR" encoding', function(done) { - checkReadWithEncoding("EUC-KR", "test", done); - }); - - // Taiwan/Hong Kong - it('should read in a file with "Big5" encoding', function(done) { - checkReadWithEncoding("Big5", "test", done); - }); - - it('should read in a file with "Big5-HKSCS" encoding', function(done) { - checkReadWithEncoding("Big5-HKSCS", "test", done); - }); - - it('should read in a file with "Windows950" encoding', function(done) { - checkReadWithEncoding("Windows950", "test", done); - }); - - // Windows - it('should read in a file with "cp874" encoding', function(done) { - checkReadWithEncoding("cp874", "test", done); - }); - - it('should read in a file with "cp1250" encoding', function(done) { - checkReadWithEncoding("cp1250", "test", done); - }); - - it('should read in a file with "cp1251" encoding', function(done) { - checkReadWithEncoding("cp1251", "test", done); - }); - - it('should read in a file with "cp1252" encoding', function(done) { - checkReadWithEncoding("cp1252", "test", done); - }); - - it('should read in a file with "cp1253" encoding', function(done) { - checkReadWithEncoding("cp1253", "test", done); - }); - - it('should read in a file with "cp1254" encoding', function(done) { - checkReadWithEncoding("cp1254", "test", done); - }); - - it('should read in a file with "cp1255" encoding', function(done) { - checkReadWithEncoding("cp1255", "test", done); - }); - - it('should read in a file with "cp1256" encoding', function(done) { - checkReadWithEncoding("cp1256", "test", done); - }); - - it('should read in a file with "cp1257" encoding', function(done) { - checkReadWithEncoding("cp1257", "test", done); - }); - - it('should read in a file with "cp1258" encoding', function(done) { - checkReadWithEncoding("cp1258", "test", done); - }); - - // IBM - it('should read in a file with "cp437" encoding', function(done) { - checkReadWithEncoding("cp437", "test", done); - }); - - it('should read in a file with "cp737" encoding', function(done) { - checkReadWithEncoding("cp737", "test", done); - }); - - it('should read in a file with "cp775" encoding', function(done) { - checkReadWithEncoding("cp775", "test", done); - }); - - it('should read in a file with "cp808" encoding', function(done) { - checkReadWithEncoding("cp808", "test", done); - }); - - it('should read in a file with "cp850" encoding', function(done) { - checkReadWithEncoding("cp850", "test", done); - }); - - it('should read in a file with "cp852" encoding', function(done) { - checkReadWithEncoding("cp852", "test", done); - }); - - it('should read in a file with "cp855" encoding', function(done) { - checkReadWithEncoding("cp855", "test", done); - }); - - it('should read in a file with "cp856" encoding', function(done) { - checkReadWithEncoding("cp856", "test", done); - }); - - it('should read in a file with "cp857" encoding', function(done) { - checkReadWithEncoding("cp857", "test", done); - }); - - it('should read in a file with "cp858" encoding', function(done) { - checkReadWithEncoding("cp858", "test", done); - }); - - it('should read in a file with "cp860" encoding', function(done) { - checkReadWithEncoding("cp860", "test", done); - }); - - it('should read in a file with "cp861" encoding', function(done) { - checkReadWithEncoding("cp861", "test", done); - }); - - it('should read in a file with "cp866" encoding', function(done) { - checkReadWithEncoding("cp866", "test", done); - }); - - it('should read in a file with "cp869" encoding', function(done) { - checkReadWithEncoding("cp869", "test", done); - }); - - it('should read in a file with "cp922" encoding', function(done) { - checkReadWithEncoding("cp922", "test", done); - }); - - it('should read in a file with "cp1046" encoding', function(done) { - checkReadWithEncoding("cp1046", "test", done); - }); - - it('should read in a file with "cp1124" encoding', function(done) { - checkReadWithEncoding("cp1124", "test", done); - }); - - it('should read in a file with "cp1125" encoding', function(done) { - checkReadWithEncoding("cp1125", "test", done); - }); - - it('should read in a file with "cp1129" encoding', function(done) { - checkReadWithEncoding("cp1129", "test", done); - }); - - it('should read in a file with "cp1133" encoding', function(done) { - checkReadWithEncoding("cp1133", "test", done); - }); - - it('should read in a file with "cp1161" encoding', function(done) { - checkReadWithEncoding("cp1161", "test", done); - }); - - it('should read in a file with "cp1162" encoding', function(done) { - checkReadWithEncoding("cp1162", "test", done); - }); - - it('should read in a file with "cp1163" encoding', function(done) { - checkReadWithEncoding("cp1163", "test", done); - }); - - // Mac - it('should read in a file with "maccroatian" encoding', function(done) { - checkReadWithEncoding("maccroatian", "test", done); - }); - - it('should read in a file with "maccyrillic" encoding', function(done) { - checkReadWithEncoding("maccyrillic", "test", done); - }); - - it('should read in a file with "macgreek" encoding', function(done) { - checkReadWithEncoding("macgreek", "test", done); - }); - - it('should read in a file with "maciceland" encoding', function(done) { - checkReadWithEncoding("maciceland", "test", done); - }); - - it('should read in a file with "macroman" encoding', function(done) { - checkReadWithEncoding("macroman", "test", done); - }); - - it('should read in a file with "macromania" encoding', function(done) { - checkReadWithEncoding("macromania", "test", done); - }); - - it('should read in a file with "macthai" encoding', function(done) { - checkReadWithEncoding("macthai", "test", done); - }); - - it('should read in a file with "macturkish" encoding', function(done) { - checkReadWithEncoding("macturkish", "test", done); - }); - - it('should read in a file with "macukraine" encoding', function(done) { - checkReadWithEncoding("macukraine", "test", done); - }); - - it('should read in a file with "maccenteuro" encoding', function(done) { - checkReadWithEncoding("maccenteuro", "test", done); - }); - - it('should read in a file with "macintosh" encoding', function(done) { - checkReadWithEncoding("macintosh", "test", done); - }); - - // KOI8 - it('should read in a file with "koi8-r" encoding', function(done) { - checkReadWithEncoding("koi8-r", "test", done); - }); - - it('should read in a file with "koi8-u" encoding', function(done) { - checkReadWithEncoding("koi8-u", "test", done); - }); - - it('should read in a file with "koi8-ru" encoding', function(done) { - checkReadWithEncoding("koi8-ru", "test", done); - }); - - it('should read in a file with "koi8-t" encoding', function(done) { - checkReadWithEncoding("koi8-t", "test", done); - }); - - // Misc - it('should read in a file with "armscii8" encoding', function(done) { - checkReadWithEncoding("armscii8", "test", done); - }); - - it('should read in a file with "rk1048" encoding', function(done) { - checkReadWithEncoding("rk1048", "test", done); - }); - - it('should read in a file with "tcvn" encoding', function(done) { - checkReadWithEncoding("tcvn", "test", done); - }); - - it('should read in a file with "georgianacademy" encoding', function(done) { - checkReadWithEncoding("georgianacademy", "test", done); - }); - - it('should read in a file with "georgianps" encoding', function(done) { - checkReadWithEncoding("georgianps", "test", done); - }); - - it('should read in a file with "pt154" encoding', function(done) { - checkReadWithEncoding("pt154", "test", done); - }); - - it('should read in a file with "viscii" encoding', function(done) { - checkReadWithEncoding("viscii", "test", done); - }); - - it('should read in a file with "iso646cn" encoding', function(done) { - checkReadWithEncoding("iso646cn", "test", done); - }); - - it('should read in a file with "iso646jp" encoding', function(done) { - checkReadWithEncoding("iso646jp", "test", done); - }); - - it('should read in a file with "hproman8" encoding', function(done) { - checkReadWithEncoding("hproman8", "test", done); - }); - - it('should read in a file with "tis620" encoding', function(done) { - checkReadWithEncoding("tis620", "test", done); - }); - - }); - - }); -}); diff --git a/test/nodes/core/storage/23-watch_spec.js b/test/nodes/core/storage/23-watch_spec.js deleted file mode 100644 index a9942d5b1..000000000 --- a/test/nodes/core/storage/23-watch_spec.js +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var fs = require("fs-extra"); -var path = require("path"); -var should = require("should"); -var helper = require("node-red-node-test-helper"); -var watchNode = require("nr-test-utils").require("@node-red/nodes/core/storage/23-watch.js"); - - -describe('watch Node', function() { - this.timeout(5000); - - var resourcesDir = path.join(__dirname,"..","..","..","resources"); - var baseDir = path.join(resourcesDir, "23-watch-test-dir"); - var count = 0; - - function prepareDir() { - var dirToWatch = path.join(baseDir, "base"+count); - fs.mkdirSync(dirToWatch); - count++; - return { - dirToWatch:dirToWatch, - file0ToWatch:path.join(dirToWatch, "file0.txt"), - file1ToWatch:path.join(dirToWatch, "file1.txt"), - subDirToWatch:path.join(dirToWatch, "subdir"), - file2ToWatch:path.join(dirToWatch, "subdir", "file2.txt") - } - } - - function wait(msec, func) { - setTimeout(func, msec); - } - - before(function(done) { - fs.ensureDirSync(baseDir); - done(); - }); - - after(function(done) { - fs.removeSync(baseDir); - done(); - }); - - afterEach(function(done) { - helper.unload(); - done(); - }); - - function testWatch(flow, change_func, results, done) { - var processed = {}; - helper.load(watchNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - var count = 0; - var len = Object.keys(results).length; - n2.on("input", function(msg) { - try { - // console.log(msg); - msg.should.have.property('file'); - - var file = msg.file; - if (file in processed) { - // multiple messages come in rare case - return; - } - processed[file] = true; - if (file === 'subdir') { - // On OSX, we get a change event on subdir when a file inside changes. - // On Travis, we don't. *sigh* - return; - } - (file in results).should.be.true(); - - var result = results[file]; - msg.should.have.property('payload', result.payload); - msg.should.have.property('type', result.type); - if('size' in result) { - msg.should.have.property('size', result.size); - } - count++; - if(count === len) { - n1.close(); - // wait for close - wait(100, done); - } - }catch(err) { - done(err); - } - }); - // wait for preparation - wait(500, change_func); - }); - } - - it('should watch a file to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.file0ToWatch, recursive: false, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file', - 'size': 5 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file0ToWatch, "ABCDE"); - }, results, done); - }); - - it('should watch multiple files to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - fs.writeFileSync(files.file1ToWatch, ''); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.file0ToWatch +","+files.file1ToWatch, recursive: false, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file'//, - // 'size': 5 - }, - 'file1.txt' : { - 'payload' : files.file1ToWatch, - 'topic': files.file1ToWatch, - 'type': 'file'//, - // 'size': 3 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file0ToWatch, "ABCDE"); - fs.appendFileSync(files.file1ToWatch, "123"); - }, results, done); - }); - - it('should watch attribute of a file to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - fs.chmodSync(files.file0ToWatch, 0o444); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.file0ToWatch, recursive: false, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file'//, - // 'size': 0 - } - }; - testWatch(flow, function() { - fs.chmodSync(files.file0ToWatch, 0o777); - }, results, done); - }); - - it('should watch a file in a directory to be changed', function(done) { - var files = prepareDir(); - fs.writeFileSync(files.file0ToWatch, ''); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.dirToWatch, recursive: true, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file0.txt' : { - 'payload' : files.file0ToWatch, - 'topic': files.file0ToWatch, - 'type': 'file'//, - // 'size': 5 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file0ToWatch, "ABCDE"); - }, results, done); - }); - - it('should watch a sub directory in a directory to be changed', function(done) { - var files = prepareDir(); - fs.mkdirSync(files.subDirToWatch); - var flow = [{id:"n1", type:"watch", name: "watch", - files: files.dirToWatch, recursive: true, - wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var results = { - 'file2.txt': { - payload: files.file2ToWatch, - type: 'file'//, - // size: 5 - } - }; - testWatch(flow, function() { - fs.appendFileSync(files.file2ToWatch, "ABCDE"); - }, results, done); - }); - -}); diff --git a/test/nodes/subflow/subflow_spec.js b/test/nodes/subflow/subflow_spec.js deleted file mode 100644 index d5b8d78ae..000000000 --- a/test/nodes/subflow/subflow_spec.js +++ /dev/null @@ -1,570 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -var should = require("should"); -var functionNode = require("nr-test-utils").require("@node-red/nodes/core/function/10-function.js"); -var helper = require("node-red-node-test-helper"); - -// Notice: -// - nodes should have x, y, z property when defining subflow. - -describe('subflow', function() { - - before(function(done) { - helper.startServer(done); - }); - - after(function(done) { - helper.stopServer(done); - }); - - afterEach(function() { - helper.unload(); - }); - - it('should define subflow', function(done) { - var flow = [ - {id:"t1", type:"tab"}, - {id:"n1", z:"t1", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", z:"t1", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{wires:[ {id:"s1-n1"} ]}], - out:[{wires:[ {id:"s1-n1", port:0} ]}]}, - {id:"s1-n1", z:"s1", type:"function", - func:"return msg;", wires:[]} - ]; - helper.load(functionNode, flow, function() { - done(); - }); - }); - - it('should pass data to/from subflow', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.payload = msg.payload+'bar'; return msg;", wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "foobar"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should pass data to/from nested subflow', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow1 - {id:"s1", type:"subflow", name:"Subflow1", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n2", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"subflow:s2", - wires:[["s1-n2"]]}, - {id:"s1-n2", x:10, y:10, z:"s1", type:"function", - func:"msg.payload = msg.payload+'baz'; return msg;", wires:[]}, - // Subflow2 - {id:"s2", type:"subflow", name:"Subflow2", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s2-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s2-n1", port:0} ] - }] - }, - {id:"s2-n1", x:10, y:10, z:"s2", type:"function", - func:"msg.payload=msg.payload+'bar'; return msg;", wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload", "foobarbaz"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of subflow template', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - env: [ - {name: "K", type: "str", value: "V"} - ], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of subflow instance', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access last env var with same name', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V0"}, - {name: "X", type: "str", value: "VX"}, - {name: "K", type: "str", value: "V1"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V1"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access typed value of env var', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "KN", type: "num", value: "100"}, - {name: "KB", type: "bool", value: "true"}, - {name: "KJ", type: "json", value: "[1,2,3]"}, - {name: "Kb", type: "bin", value: "[65,65]"}, - {name: "Ke", type: "env", value: "KS"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }], - env: [ - {name: "KS", type: "str", value: "STR"} - ] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.VE = env.get('Ke'); msg.VS = env.get('KS'); msg.VN = env.get('KN'); msg.VB = env.get('KB'); msg.VJ = env.get('KJ'); msg.Vb = env.get('Kb'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("VS", "STR"); - msg.should.have.property("VN", 100); - msg.should.have.property("VB", true); - msg.should.have.property("VJ", [1,2,3]); - msg.should.have.property("Vb"); - should.ok(msg.Vb instanceof Buffer); - msg.should.have.property("VE","STR"); - done(); - } - catch (e) { - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should overwrite env var of subflow template by env var of subflow instance', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", - env: [ - {name: "K", type: "str", value: "TV"} - ], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of parent subflow template', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow1 - {id:"s1", type:"subflow", name:"Subflow1", info:"", - env: [ - {name: "K", type: "str", value: "V"}, - ], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n2", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"subflow:s2", - wires:[["s1-n2"]]}, - {id:"s1-n2", x:10, y:10, z:"s1", type:"function", - func:"return msg;", wires:[]}, - // Subflow2 - {id:"s2", type:"subflow", name:"Subflow2", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s2-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s2-n1", port:0} ] - }] - }, - {id:"s2-n1", x:10, y:10, z:"s2", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("V", "V"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of parent subflow instance', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", - env: [ - {name: "K", type: "str", value: "V"} - ], - wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow1 - {id:"s1", type:"subflow", name:"Subflow1", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n2", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"subflow:s2", - wires:[["s1-n2"]]}, - {id:"s1-n2", x:10, y:10, z:"s1", type:"function", - func:"return msg;", wires:[]}, - // Subflow2 - {id:"s2", type:"subflow", name:"Subflow2", info:"", - in:[{ - x:10, y:10, - wires:[ {id:"s2-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s2-n1", port:0} ] - }] - }, - {id:"s2-n1", x:10, y:10, z:"s2", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("V", "V"); - done(); - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of tab', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:"", env: [ - {name: "K", type: "str", value: "V"} - ]}, - {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", env: [], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of group', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"g1", z:"t0", type:"group", env:[ - {name: "K", type: "str", value: "V"} - ]}, - {id:"n1", x:10, y:10, z:"t0", g:"g1", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", env: [], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - - it('should access env var of nested group', function(done) { - var flow = [ - {id:"t0", type:"tab", label:"", disabled:false, info:""}, - {id:"g1", z:"t0", type:"group", env:[ - {name: "K", type: "str", value: "V"} - ]}, - {id:"g2", z:"t0", g:"g1", type:"group", env:[]}, - {id:"n1", x:10, y:10, z:"t0", g:"g2", type:"subflow:s1", wires:[["n2"]]}, - {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, - // Subflow - {id:"s1", type:"subflow", name:"Subflow", info:"", env: [], - in:[{ - x:10, y:10, - wires:[ {id:"s1-n1"} ] - }], - out:[{ - x:10, y:10, - wires:[ {id:"s1-n1", port:0} ] - }] - }, - {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.V = env.get('K'); return msg;", - wires:[]} - ]; - helper.load(functionNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - try { - msg.should.have.property("V", "V"); - done(); - } - catch (e) { - console.log(e); - done(e); - } - }); - n1.receive({payload:"foo"}); - }); - }); - -}); diff --git a/test/resources/70-HTML-test-file.html b/test/resources/70-HTML-test-file.html deleted file mode 100644 index 2187b8e31..000000000 --- a/test/resources/70-HTML-test-file.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - -

    This is a test page for node 70-HTML

    - -

    There's nothing to read here.

    - -
      -
    1. Blue
    2. -
    3. Red
    4. -
    - -
      -
    • Apple
    • -
    • Pear
    • -
    - -
      -
    • Potato
    • -
    • Parsnip
    • -
    - - - - - - - diff --git a/test/resources/file-in-node/test.txt b/test/resources/file-in-node/test.txt deleted file mode 100644 index 68ca53b4d..000000000 --- a/test/resources/file-in-node/test.txt +++ /dev/null @@ -1 +0,0 @@ -Text file \ No newline at end of file diff --git a/test/resources/icons/test_icon.png b/test/resources/icons/test_icon.png deleted file mode 100644 index 4b6b7b5e9b10ec790348eb59f73bdac596e2f3b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1$P6UK?yS)OQjEnx?oJHr&dIz4a@YcVLR^9L z|NsA&-kg6IB%S2#?!wT)D(eB{a29w(76Vni0bxeDQVUa{AbW|YuPggKZdPtVo=;b% zu>pm|JY5_^IIbrr*#Br`l>NUU`M`mYFm{Gnm$g4<{K@tLs$lSR^>bP0l+XkK5O^!s diff --git a/test/resources/plugin/test-plugin/library-filestore.html b/test/resources/plugin/test-plugin/library-filestore.html deleted file mode 100644 index 60fbae464..000000000 --- a/test/resources/plugin/test-plugin/library-filestore.html +++ /dev/null @@ -1,22 +0,0 @@ - \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/library-filestore.js b/test/resources/plugin/test-plugin/library-filestore.js deleted file mode 100644 index 7c6f1db83..000000000 --- a/test/resources/plugin/test-plugin/library-filestore.js +++ /dev/null @@ -1,37 +0,0 @@ - -module.exports = function(RED) { - const PLUGIN_TYPE_ID = "node-red-library-filestore"; - - class FileStorePlugin { - constructor(config) { - this.type = PLUGIN_TYPE_ID; - this.id = config.id; - this.label = config.label; - this.config = config.config; - this.icon = config.icon; - - console.log("FileStorePlugin",config) - } - async init() { - console.log("FileStorePlugin.init") - - } - async getEntry(type,path) { - console.log("FileStorePlugin.getLibraryEntry",type,path) - return [] - } - async saveEntry(type,path,meta,body) { - console.log("FileStorePlugin.saveLibraryEntry",type,path) - } - } - - - RED.plugins.registerPlugin(PLUGIN_TYPE_ID, { - type: "node-red-library-source", - class: FileStorePlugin, - defaults: { - "path": { value: "" }, - // "secret": { type: "password" } - } - }) -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/locales/en-US/library-filestore.json b/test/resources/plugin/test-plugin/locales/en-US/library-filestore.json deleted file mode 100644 index b9ffa5f17..000000000 --- a/test/resources/plugin/test-plugin/locales/en-US/library-filestore.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": { - "path": "Path" - }, - "desc": { - "path":"The local file-system path to the library" - } -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json b/test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json deleted file mode 100644 index e990e2ec1..000000000 --- a/test/resources/plugin/test-plugin/locales/en-US/test-editor-plugin.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugin": "winning" -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/package.json b/test/resources/plugin/test-plugin/package.json deleted file mode 100644 index 65487c656..000000000 --- a/test/resources/plugin/test-plugin/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "test-plugin", - "version": "1.0.0", - "description": "", - "node-red": { - "plugins": { - "test": "test.js", - "test-editor-plugin": "test-editor-plugin.html", - "test-runtime-plugin": "test-runtime-plugin.js", - "library-filestore": "library-filestore.js" - } - } -} diff --git a/test/resources/plugin/test-plugin/test-editor-plugin.html b/test/resources/plugin/test-plugin/test-editor-plugin.html deleted file mode 100644 index 177813526..000000000 --- a/test/resources/plugin/test-plugin/test-editor-plugin.html +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/test-runtime-plugin.js b/test/resources/plugin/test-plugin/test-runtime-plugin.js deleted file mode 100644 index d9e3ff3c7..000000000 --- a/test/resources/plugin/test-plugin/test-runtime-plugin.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = function(RED) { - console.log("Loaded test-plugin/test-runtime-plugin") - - RED.plugins.registerPlugin("my-test-runtime-only-plugin", { - type: "bar", - onadd: function() { - console.log("my-test-runtime-only-plugin.onadd called") - } - }) -} \ No newline at end of file diff --git a/test/resources/plugin/test-plugin/test.html b/test/resources/plugin/test-plugin/test.html deleted file mode 100644 index 7a43a0528..000000000 --- a/test/resources/plugin/test-plugin/test.html +++ /dev/null @@ -1,6 +0,0 @@ - - -"); - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns node module info', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleInfo: function(opts) { - return Promise.resolve({"node-red":{name:"node-red"}}[opts.module]); - } - } - }); - request(app) - .get('/nodes/node-red') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - done(); - }); - }); - - it('returns 404 for unknown module', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleInfo: function(opts) { - var errInstance = new Error("Not Found"); - errInstance.code = "not_found"; - errInstance.status = 404; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .get('/nodes/node-blue') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns individual node info', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getNodeInfo: function(opts) { - return Promise.resolve({"node-red/123":{id:"node-red/123"}}[opts.id]); - } - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","node-red/123"); - done(); - }); - }); - - it('returns individual node configs', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getNodeConfig: function(opts) { - return Promise.resolve({"node-red/123":""}[opts.id]); - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - it('returns 404 for unknown node', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getNodeInfo: function(opts) { - var errInstance = new Error("Not Found"); - errInstance.code = "not_found"; - errInstance.status = 404; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .get('/nodes/node-red/456') - .set('Accept', 'application/json') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - }); - - describe('install', function() { - it('installs the module and returns module info', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - addModule: function(_opts) { - opts = _opts; - return Promise.resolve({ - name:"foo", - nodes:[{id:"123"}] - }); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","foo"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("id","123"); - opts.should.have.property("module","foo"); - opts.should.have.property("version","1.2.3"); - opts.should.have.property("url","https://example/foo-1.2.3.tgz"); - done(); - }); - }); - it('returns error', function(done) { - nodes.init({ - settings: {}, - nodes:{ - addModule: function(opts) { - var errInstance = new Error("Message"); - errInstance.code = "random_error"; - errInstance.status = 400; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo',version:"1.2.3",url:"https://example/foo-1.2.3.tgz"}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.a.property('code','random_error'); - done(); - }); - }); - }); - describe('delete', function() { - it('uninstalls the module', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - removeModule: function(_opts) { - opts = _opts; - return Promise.resolve(); - } - } - }); - request(app) - .del('/nodes/123') - .expect(204) - .end(function(err,res) { - if (err) { - throw err; - } - opts.should.have.property("module","123"); - done(); - }); - }); - it('returns error', function(done) { - nodes.init({ - settings: {}, - nodes:{ - removeModule: function(opts) { - var errInstance = new Error("Message"); - errInstance.code = "random_error"; - errInstance.status = 400; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .del('/nodes/123') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.a.property('code','random_error'); - done(); - }); - }); - }); - - describe('enable/disable node set', function() { - it('returns 400 for invalid request payload', function(done) { - nodes.init({ - settings: {}, - nodes:{ - setNodeSetState: function(opts) {return Promise.resolve()} - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("code","invalid_request"); - res.body.should.have.property("message","Invalid request"); - done(); - }); - }); - - it('sets node state and returns node info', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - setNodeSetState: function(_opts) { - opts = _opts; - return Promise.resolve({id:"123",enabled: true }); - } - } - }); - - request(app) - .put('/nodes/node-red/foo') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",true); - opts.should.have.property("enabled",true); - opts.should.have.property("id","node-red/foo"); - - done(); - }); - }); - }); - describe('enable/disable module' ,function() { - it('returns 400 for invalid request payload', function(done) { - nodes.init({ - settings: {}, - nodes:{ - setModuleState: function(opts) {return Promise.resolve()} - } - }); - request(app) - .put('/nodes/node-red') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("code","invalid_request"); - res.body.should.have.property("message","Invalid request"); - done(); - }); - }); - it('sets module state and returns module info', function(done) { - var opts; - nodes.init({ - settings: {}, - nodes:{ - setModuleState: function(_opts) { - opts = _opts; - return Promise.resolve({name:"node-red"}); - } - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - opts.should.have.property("enabled",true); - opts.should.have.property("module","node-red"); - - done(); - }); - }); - }); - - describe('get icons', function() { - it('returns icon list', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getIconList: function() { - return Promise.resolve({module:[1,2,3]}); - } - } - }); - request(app) - .get('/getIcons') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("module"); - res.body.module.should.be.an.Array(); - res.body.module.should.have.lengthOf(3); - done(); - }); - }); - }); - - describe('get module messages', function() { - it('returns message catalog', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleCatalog: function(opts) { - return Promise.resolve({a:123}); - } - } - }); - request(app) - .get('/nodes/module/set/messages') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.eql({a:123}); - done(); - }); - }); - it('returns all node catalogs', function(done) { - nodes.init({ - settings: {}, - nodes:{ - getModuleCatalogs: function(opts) { - return Promise.resolve({a:1}); - } - } - }); - request(app) - .get('/nodes/messages') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.eql({a:1}); - done(); - }); - }); - }) -}); diff --git a/test/unit/@node-red/editor-api/lib/admin/plugins_spec.js b/test/unit/@node-red/editor-api/lib/admin/plugins_spec.js deleted file mode 100644 index 74584a1d8..000000000 --- a/test/unit/@node-red/editor-api/lib/admin/plugins_spec.js +++ /dev/null @@ -1,111 +0,0 @@ -const should = require("should"); -const request = require('supertest'); -const express = require('express'); -const bodyParser = require("body-parser"); - -var app; - -var NR_TEST_UTILS = require("nr-test-utils"); - -var plugins = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/plugins"); - -describe("api/editor/plugins", function() { - const pluginList = [ - { - "id": "test-module/test-set", - "enabled": true, - "local": false, - "plugins": [ - { - "type": "foo", - "id": "a-plugin", - "module": "test-module" - }, - { - "type": "bar", - "id": "a-plugin2", - "module": "test-module" - }, - { - "type": "foo", - "id": "a-plugin3", - "module": "test-module" - } - ] - }, - { - "id": "test-module/test-disabled-set", - "enabled": false, - "local": false, - "plugins": [] - } - ]; - const pluginConfigs = ` - -test-module-config`; - - const pluginCatalogs = { "test-module": {"foo": "bar"}}; - - before(function() { - app = express(); - app.use(bodyParser.json()); - app.get("/plugins",plugins.getAll); - app.get("/plugins/messages",plugins.getCatalogs); - - plugins.init({ - plugins: { - getPluginList: async function() { return pluginList }, - getPluginConfigs: async function() { return pluginConfigs }, - getPluginCatalogs: async function() { return pluginCatalogs } - } - }) - }); - - it('returns the list of plugins', function(done) { - request(app) - .get("/plugins") - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - try { - JSON.stringify(res.body).should.eql(JSON.stringify(pluginList)); - done(); - } catch(err) { - done(err) - } - }); - }); - it('returns the plugin configs', function(done) { - request(app) - .get("/plugins") - .set('Accept', 'text/html') - .expect(200) - .expect(pluginConfigs) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - it('returns the plugin catalogs', function(done) { - request(app) - .get("/plugins/messages") - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - try { - JSON.stringify(res.body).should.eql(JSON.stringify(pluginCatalogs)); - done(); - } catch(err) { - done(err) - } - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/admin/settings_spec.js b/test/unit/@node-red/editor-api/lib/admin/settings_spec.js deleted file mode 100644 index 6180f903d..000000000 --- a/test/unit/@node-red/editor-api/lib/admin/settings_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require("body-parser"); -var sinon = require('sinon'); - -var app; - -var NR_TEST_UTILS = require("nr-test-utils"); - -var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/settings"); -var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); - -describe("api/editor/settings", function() { - before(function() { - sinon.stub(theme,"settings").callsFake(function() { return { existing: 123, test: 456 };}); - app = express(); - app.use(bodyParser.json()); - app.get("/settings",info.runtimeSettings); - }); - - after(function() { - theme.settings.restore(); - }); - - it('returns the runtime settings', function(done) { - info.init({},{ - settings: { - getRuntimeSettings: function(opts) { - return Promise.resolve({ - a:1, - b:2, - editorTheme: { existing: 789 } - }) - } - } - }); - request(app) - .get("/settings") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("a",1); - res.body.should.have.property("b",2); - res.body.should.have.property("editorTheme",{existing: 789, test:456}); - done(); - }); - }); - it('returns the runtime settings - disableEditor true', function(done) { - info.init({disableEditor: true},{ - settings: { - getRuntimeSettings: function(opts) { - return Promise.resolve({ - a:1, - b:2 - }) - } - } - }); - request(app) - .get("/settings") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("a",1); - res.body.should.have.property("b",2); - // no editorTheme if disabledEditor true - res.body.should.not.have.property("editorTheme"); - done(); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/clients_spec.js b/test/unit/@node-red/editor-api/lib/auth/clients_spec.js deleted file mode 100644 index 1f6263d00..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/clients_spec.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var NR_TEST_UTILS = require("nr-test-utils"); -var Clients = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/clients"); - -describe("api/auth/clients", function() { - it('finds the known editor client',function(done) { - Clients.get("node-red-editor").then(function(client) { - client.should.have.property("id","node-red-editor"); - client.should.have.property("secret","not_available"); - done(); - }); - }); - it('finds the known admin client',function(done) { - Clients.get("node-red-admin").then(function(client) { - client.should.have.property("id","node-red-admin"); - client.should.have.property("secret","not_available"); - done(); - }).catch(function(err) { - done(err); - }); - }); - it('returns null for unknown client',function(done) { - Clients.get("unknown-client").then(function(client) { - should.not.exist(client); - done(); - }).catch(function(err) { - done(err); - }); - - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/index_spec.js b/test/unit/@node-red/editor-api/lib/auth/index_spec.js deleted file mode 100644 index 010b6b08a..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/index_spec.js +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var passport = require("passport"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth"); -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); -var Permissions = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/permissions"); - -describe("api/auth/index",function() { - - - - describe("ensureClientSecret", function() { - before(function() { - auth.init({},{}) - }); - it("leaves client_secret alone if not present",function(done) { - var req = { - body: { - client_secret: "test_value" - } - }; - auth.ensureClientSecret(req,null,function() { - req.body.should.have.a.property("client_secret","test_value"); - done(); - }) - }); - it("applies a default client_secret if not present",function(done) { - var req = { - body: { } - }; - auth.ensureClientSecret(req,null,function() { - req.body.should.have.a.property("client_secret","not_available"); - done(); - }) - }); - }); - - describe("revoke", function() { - it("revokes a token", function(done) { - var revokeToken = sinon.stub(Tokens,"revoke").callsFake(function() { - return Promise.resolve(); - }); - - var req = { body: { token: "abcdef" } }; - - var res = { status: function(resp) { - revokeToken.restore(); - - resp.should.equal(200); - return { - end: done - } - }}; - - auth.revoke(req,res); - }); - }); - - describe("login", function() { - beforeEach(function() { - sinon.stub(Tokens,"init").callsFake(function(){}); - sinon.stub(Users,"init").callsFake(function(){}); - }); - afterEach(function() { - Tokens.init.restore(); - Users.init.restore(); - }); - it("returns login details - credentials", function(done) { - auth.init({adminAuth:{type:"credentials"}},{}) - auth.login(null,{json: function(resp) { - resp.should.have.a.property("type","credentials"); - resp.should.have.a.property("prompts"); - resp.prompts.should.have.a.lengthOf(2); - done(); - }}); - }); - it("returns login details - none", function(done) { - auth.init({},{}) - auth.login(null,{json: function(resp) { - resp.should.eql({}); - done(); - }}); - }); - it("returns login details - strategy", function(done) { - auth.init({adminAuth:{type:"strategy",strategy:{label:"test-strategy",icon:"test-icon"}}},{}) - auth.login(null,{json: function(resp) { - resp.should.have.a.property("type","strategy"); - resp.should.have.a.property("prompts"); - resp.prompts.should.have.a.lengthOf(1); - resp.prompts[0].should.have.a.property("type","button"); - resp.prompts[0].should.have.a.property("label","test-strategy"); - resp.prompts[0].should.have.a.property("icon","test-icon"); - - done(); - }}); - }); - - }); - describe("needsPermission", function() { - beforeEach(function() { - sinon.stub(Tokens,"init").callsFake(function(){}); - sinon.stub(Users,"init").callsFake(function(){}); - }); - afterEach(function() { - Tokens.init.restore(); - Users.init.restore(); - if (passport.authenticate.restore) { - passport.authenticate.restore(); - } - if (Permissions.hasPermission.restore) { - Permissions.hasPermission.restore(); - } - }); - - - it('no-ops if adminAuth not set', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - } - }); - auth.init({}); - var func = auth.needsPermission("foo"); - func({},{},function() { - passport.authenticate.called.should.be.false(); - done(); - }) - }); - it('skips auth if req.user undefined', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - next(); - } - }); - sinon.stub(Permissions,"hasPermission").callsFake(function(perm) { return true }); - auth.init({adminAuth:{}}); - var func = auth.needsPermission("foo"); - func({user:null},{},function() { - try { - passport.authenticate.called.should.be.true(); - Permissions.hasPermission.called.should.be.false(); - done(); - } catch(err) { - done(err); - } - }) - }); - - it('passes for valid user permission', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - next(); - } - }); - sinon.stub(Permissions,"hasPermission").callsFake(function(perm) { return true }); - auth.init({adminAuth:{}}); - var func = auth.needsPermission("foo"); - func({user:true,authInfo: { scope: "read"}},{},function() { - try { - passport.authenticate.called.should.be.true(); - Permissions.hasPermission.called.should.be.true(); - Permissions.hasPermission.lastCall.args[0].should.eql("read"); - Permissions.hasPermission.lastCall.args[1].should.eql("foo"); - done(); - } catch(err) { - done(err); - } - }) - }); - - it('rejects for invalid user permission', function(done) { - sinon.stub(passport,"authenticate").callsFake(function(scopes,opts) { - return function(req,res,next) { - next(); - } - }); - sinon.stub(Permissions,"hasPermission").callsFake(function(perm) { return false }); - auth.init({adminAuth:{}}); - var func = auth.needsPermission("foo"); - func({user:true,authInfo: { scope: "read"}},{ - status: function(status) { - return { end: function() { - try { - status.should.eql(401); - done(); - } catch(err) { - done(err); - } - }} - } - },function() { - done(new Error("hasPermission unexpected passed")) - }); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/permissions_spec.js b/test/unit/@node-red/editor-api/lib/auth/permissions_spec.js deleted file mode 100644 index 50c249c34..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/permissions_spec.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var permissions = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/permissions"); - -describe("api/auth/permissions", function() { - describe("hasPermission", function() { - it('a user with no permissions',function() { - permissions.hasPermission([],"*").should.be.false(); - }); - it('a user with global permissions',function() { - permissions.hasPermission("*","read").should.be.true(); - permissions.hasPermission(["*"],"write").should.be.true(); - }); - it('a user with read permissions',function() { - permissions.hasPermission(["read"],"read").should.be.true(); - permissions.hasPermission(["read"],"node.read").should.be.true(); - permissions.hasPermission(["read"],"write").should.be.false(); - permissions.hasPermission(["read"],"node.write").should.be.false(); - permissions.hasPermission(["*.read"],"read").should.be.true(); - permissions.hasPermission(["*.read"],"node.read").should.be.true(); - permissions.hasPermission(["*.read"],"write").should.be.false(); - permissions.hasPermission(["*.read"],"node.write").should.be.false(); - }); - it('a user with foo permissions',function() { - permissions.hasPermission("foo","foo").should.be.true(); - }); - it('an array of permissions', function() { - permissions.hasPermission(["*"],["foo.read","foo.write"]).should.be.true(); - permissions.hasPermission("read",["foo.read","foo.write"]).should.be.false(); - permissions.hasPermission("read",["foo.read","bar.read"]).should.be.true(); - permissions.hasPermission(["flows.read"],["flows.read"]).should.be.true(); - permissions.hasPermission(["flows.read"],["flows.write"]).should.be.false(); - permissions.hasPermission(["flows.read","nodes.write"],["flows.write"]).should.be.false(); - permissions.hasPermission(["flows.read","nodes.write"],["nodes.write"]).should.be.true(); - }); - it('permits an empty permission', function() { - permissions.hasPermission("*","").should.be.true(); - permissions.hasPermission("read",[""]).should.be.true(); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/strategies_spec.js b/test/unit/@node-red/editor-api/lib/auth/strategies_spec.js deleted file mode 100644 index 9fafc11ed..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/strategies_spec.js +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var strategies = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/strategies"); -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); -var Clients = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/clients"); - -describe("api/auth/strategies", function() { - describe("Password Token Exchange", function() { - var userAuthentication; - afterEach(function() { - if (userAuthentication) { - userAuthentication.restore(); - userAuthentication = null; - } - }); - - it('Handles authentication failure',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve(null); - }); - - strategies.passwordTokenExchange({},"user","password","scope",function(err,token) { - try { - should.not.exist(err); - token.should.be.false(); - done(); - } catch(e) { - done(e); - } - }); - }); - - it('Handles scope overreach',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve({username:"user",permissions:"read"}); - }); - - strategies.passwordTokenExchange({},"user","password","*",function(err,token) { - try { - should.not.exist(err); - token.should.be.false(); - done(); - } catch(e) { - done(e); - } - }); - }); - - it('Creates new token on authentication success',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve({username:"user",permissions:"*"}); - }); - var tokenDetails = {}; - var tokenCreate = sinon.stub(Tokens,"create").callsFake(function(username,client,scope) { - tokenDetails.username = username; - tokenDetails.client = client; - tokenDetails.scope = scope; - return Promise.resolve({accessToken: "123456"}); - }); - - strategies.passwordTokenExchange({id:"myclient"},"user","password","read",function(err,token) { - try { - should.not.exist(err); - token.should.equal("123456"); - tokenDetails.should.have.property("username","user"); - tokenDetails.should.have.property("client","myclient"); - tokenDetails.should.have.property("scope","read"); - done(); - } catch(e) { - done(e); - } finally { - tokenCreate.restore(); - } - }); - - }); - }); - - describe("Anonymous Strategy", function() { - it('Succeeds if anon user enabled',function(done) { - var userDefault = sinon.stub(Users,"default").callsFake(function() { - return Promise.resolve("anon"); - }); - strategies.anonymousStrategy._success = strategies.anonymousStrategy.success; - strategies.anonymousStrategy.success = function(user) { - user.should.equal("anon"); - strategies.anonymousStrategy.success = strategies.anonymousStrategy._success; - delete strategies.anonymousStrategy._success; - done(); - }; - strategies.anonymousStrategy.authenticate({}); - }); - it('Fails if anon user not enabled',function(done) { - var userDefault = sinon.stub(Users,"default").callsFake(function() { - return Promise.resolve(null); - }); - strategies.anonymousStrategy._fail = strategies.anonymousStrategy.fail; - strategies.anonymousStrategy.fail = function(err) { - err.should.equal(401); - strategies.anonymousStrategy.fail = strategies.anonymousStrategy._fail; - delete strategies.anonymousStrategy._fail; - done(); - }; - strategies.anonymousStrategy.authenticate({}); - }); - afterEach(function() { - Users.default.restore(); - }) - }); - - describe("Tokens Strategy", function() { - it('Succeeds if tokens user enabled custom header',function(done) { - var userTokens = sinon.stub(Users,"tokens").callsFake(function(token) { - return Promise.resolve("tokens-"+token); - }); - var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) { - return "x-test-token"; - }); - strategies.tokensStrategy._success = strategies.tokensStrategy.success; - strategies.tokensStrategy.success = function(user) { - user.should.equal("tokens-1234"); - strategies.tokensStrategy.success = strategies.tokensStrategy._success; - delete strategies.tokensStrategy._success; - done(); - }; - strategies.tokensStrategy.authenticate({headers:{"x-test-token":"1234"}}); - }); - it('Succeeds if tokens user enabled default header',function(done) { - var userTokens = sinon.stub(Users,"tokens").callsFake(function(token) { - return Promise.resolve("tokens-"+token); - }); - var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) { - return "authorization"; - }); - strategies.tokensStrategy._success = strategies.tokensStrategy.success; - strategies.tokensStrategy.success = function(user) { - user.should.equal("tokens-1234"); - strategies.tokensStrategy.success = strategies.tokensStrategy._success; - delete strategies.tokensStrategy._success; - done(); - }; - strategies.tokensStrategy.authenticate({headers:{"authorization":"Bearer 1234"}}); - }); - it('Fails if tokens user not enabled',function(done) { - var userTokens = sinon.stub(Users,"tokens").callsFake(function() { - return Promise.resolve(null); - }); - var userTokenHeader = sinon.stub(Users,"tokenHeader").callsFake(function(token) { - return "authorization"; - }); - strategies.tokensStrategy._fail = strategies.tokensStrategy.fail; - strategies.tokensStrategy.fail = function(err) { - err.should.equal(401); - strategies.tokensStrategy.fail = strategies.tokensStrategy._fail; - delete strategies.tokensStrategy._fail; - done(); - }; - strategies.tokensStrategy.authenticate({headers:{"authorization":"Bearer 1234"}}); - }); - afterEach(function() { - Users.tokens.restore(); - Users.tokenHeader.restore(); - }) - }); - - describe("Bearer Strategy", function() { - it('Rejects invalid token',function(done) { - var getToken = sinon.stub(Tokens,"get").callsFake(function(token) { - return Promise.resolve(null); - }); - - strategies.bearerStrategy("1234",function(err,user) { - try { - should.not.exist(err); - user.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - getToken.restore(); - } - }); - }); - it('Accepts valid token',function(done) { - var getToken = sinon.stub(Tokens,"get").callsFake(function(token) { - return Promise.resolve({user:"user",scope:"scope"}); - }); - var getUser = sinon.stub(Users,"get").callsFake(function(username) { - return Promise.resolve("aUser"); - }); - - strategies.bearerStrategy("1234",function(err,user,opts) { - try { - should.not.exist(err); - user.should.equal("aUser"); - opts.should.have.a.property("scope","scope"); - done(); - } catch(e) { - done(e); - } finally { - getToken.restore(); - getUser.restore(); - } - }); - }); - it('Fail if no user for token',function(done) { - var getToken = sinon.stub(Tokens,"get").callsFake(function(token) { - return Promise.resolve({user:"user",scope:"scope"}); - }); - var getUser = sinon.stub(Users,"get").callsFake(function(username) { - return Promise.resolve(null); - }); - - strategies.bearerStrategy("1234",function(err,user,opts) { - try { - should.not.exist(err); - user.should.equal(false); - should.not.exist(opts); - done(); - } catch(e) { - done(e); - } finally { - getToken.restore(); - getUser.restore(); - } - }); - }); - }); - - describe("Client Password Strategy", function() { - it('Accepts valid client',function(done) { - var testClient = {id:"node-red-editor",secret:"not_available"}; - var getClient = sinon.stub(Clients,"get").callsFake(function(client) { - return Promise.resolve(testClient); - }); - - strategies.clientPasswordStrategy(testClient.id,testClient.secret,function(err,client) { - try { - should.not.exist(err); - client.should.eql(testClient); - done(); - } catch(e) { - done(e); - } finally { - getClient.restore(); - } - }); - }); - it('Rejects invalid client secret',function(done) { - var testClient = {id:"node-red-editor",secret:"not_available"}; - var getClient = sinon.stub(Clients,"get").callsFake(function(client) { - return Promise.resolve(testClient); - }); - - strategies.clientPasswordStrategy(testClient.id,"invalid_secret",function(err,client) { - try { - should.not.exist(err); - client.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - getClient.restore(); - } - }); - }); - it('Rejects invalid client id',function(done) { - var getClient = sinon.stub(Clients,"get").callsFake(function(client) { - return Promise.resolve(null); - }); - strategies.clientPasswordStrategy("invalid_id","invalid_secret",function(err,client) { - try { - should.not.exist(err); - client.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - getClient.restore(); - } - }); - }); - - var userAuthentication; - it('Blocks after 5 failures',function(done) { - userAuthentication = sinon.stub(Users,"authenticate").callsFake(function(username,password) { - return Promise.resolve(null); - }); - for (var z=0; z<5; z++) { - strategies.passwordTokenExchange({},"user","badpassword","scope",function(err,token) { - }); - } - strategies.passwordTokenExchange({},"user","badpassword","scope",function(err,token) { - try { - err.toString().should.equal("Error: Too many login attempts. Wait 10 minutes and try again"); - token.should.be.false(); - done(); - } catch(e) { - done(e); - } finally { - userAuthentication.restore(); - } - }); - }); - - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/tokens_spec.js b/test/unit/@node-red/editor-api/lib/auth/tokens_spec.js deleted file mode 100644 index 9bb231eb5..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/tokens_spec.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); - - -describe("api/auth/tokens", function() { - describe("#init",function() { - it('loads sessions', function(done) { - Tokens.init({}).then(done); - }); - }); - - - describe("#get",function() { - it('returns a valid token', function(done) { - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({"1234":{"user":"fred","expires":Date.now()+1000}}); - } - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - token.should.have.a.property("user","fred"); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - it('returns null for an invalid token', function(done) { - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({}); - } - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - should.not.exist(token); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - it('returns null for an expired token', function(done) { - var saveSessions = sinon.stub().returns(Promise.resolve()); - var expiryTime = Date.now()+50; - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({"1234":{"user":"fred","expires":expiryTime}}); - }, - saveSessions: saveSessions - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - should.exist(token); - setTimeout(function() { - Tokens.get("1234").then(function(token) { - try { - should.not.exist(token); - saveSessions.calledOnce.should.be.true(); - done(); - } catch(err) { - done(err); - } - }); - },100); - } catch(err) { - done(err); - } - }); - }); - }); - - it('returns a valid api token', function(done) { - Tokens.init({ - tokens: [{ - token: "1234", - user: "fred", - }] - },{ - getSessions:function() { - return Promise.resolve({}); - } - }).then(function() { - Tokens.get("1234").then(function(token) { - try { - token.should.have.a.property("user","fred"); - done(); - } catch(err) { - done(err); - } - }); - }); - - }); - }); - - describe("#create",function() { - it('creates a token', function(done) { - var savedSession; - Tokens.init({sessionExpiryTime: 10},{ - getSessions:function() { - return Promise.resolve({}); - }, - saveSessions:function(sess) { - savedSession = sess; - return Promise.resolve(); - } - }); - var expectedExpiryTime = Date.now()+10000; - - - Tokens.create("user","client","scope").then(function(token) { - try { - should.exist(savedSession); - var sessionKeys = Object.keys(savedSession); - sessionKeys.should.have.lengthOf(1); - - token.should.have.a.property('accessToken',sessionKeys[0]); - savedSession[sessionKeys[0]].should.have.a.property('user','user'); - savedSession[sessionKeys[0]].should.have.a.property('client','client'); - savedSession[sessionKeys[0]].should.have.a.property('scope','scope'); - savedSession[sessionKeys[0]].should.have.a.property('expires'); - savedSession[sessionKeys[0]].expires.should.be.within(expectedExpiryTime-200,expectedExpiryTime+200); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - describe("#revoke", function() { - it('revokes a token', function(done) { - var savedSession; - Tokens.init({},{ - getSessions:function() { - return Promise.resolve({"1234":{"user":"fred","expires":Date.now()+1000}}); - }, - saveSessions:function(sess) { - savedSession = sess; - return Promise.resolve(); - } - }).then(function() { - Tokens.revoke("1234").then(function() { - try { - savedSession.should.not.have.a.property("1234"); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/auth/users_spec.js b/test/unit/@node-red/editor-api/lib/auth/users_spec.js deleted file mode 100644 index e07a35a03..000000000 --- a/test/unit/@node-red/editor-api/lib/auth/users_spec.js +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); - -describe("api/auth/users", function() { - after(function() { - Users.init({}); - }) - describe('Initalised with a credentials object, no anon',function() { - before(function() { - Users.init({ - type:"credentials", - users:{ - username:"fred", - password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK', - // 'password' -> require('bcryptjs').hashSync('password', 8); - permissions:"*" - } - }); - }); - describe('#get',function() { - it('returns known user',function(done) { - Users.get("fred").then(function(user) { - try { - user.should.have.a.property("username","fred"); - user.should.have.a.property("permissions","*"); - user.should.not.have.a.property("password"); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('returns null for unknown user', function(done) { - Users.get("barney").then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - describe('#default',function() { - it('returns null for default user', function(done) { - Users.default().then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - - describe('#authenticate',function() { - it('authenticates a known user', function(done) { - Users.authenticate('fred','password').then(function(user) { - try { - user.should.have.a.property("username","fred"); - user.should.have.a.property("permissions","*"); - user.should.not.have.a.property("password"); - done(); - } catch(err) { - done(err); - } - }); - }); - it('rejects invalid password for a known user', function(done) { - Users.authenticate('fred','wrong').then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('rejects invalid user', function(done) { - Users.authenticate('barney','wrong').then(function(user) { - try { - should.not.exist(user); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initalised with a credentials object including anon',function() { - before(function() { - Users.init({ - type:"credentials", - users:[], - default: { permissions: "*" } - }); - }); - describe('#default',function() { - it('returns default user', function(done) { - Users.default().then(function(user) { - try { - user.should.have.a.property('anonymous',true); - user.should.have.a.property('permissions','*'); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initialised with a credentials object with user functions',function() { - var authUsername = ''; - var authPassword = ''; - before(function() { - Users.init({ - type:"credentials", - users:function(username) { - return Promise.resolve({'username':'dave','permissions':'read'}); - }, - authenticate: function(username,password) { - authUsername = username; - authPassword = password; - return Promise.resolve({'username':'pete','permissions':'write'}); - } - }); - }); - - describe('#get',function() { - it("returns null for tokenHeader", function() { - should.not.exist(Users.tokenHeader()); - }); - - it('delegates get user',function(done) { - Users.get('dave').then(function(user) { - try { - user.should.have.a.property("username","dave"); - user.should.have.a.property("permissions","read"); - user.should.not.have.a.property("password"); - done(); - } catch(err) { - done(err); - } - }); - }); - it('delegates authenticate user',function(done) { - Users.authenticate('pete','secret').then(function(user) { - try { - user.should.have.a.property("username","pete"); - user.should.have.a.property("permissions","write"); - user.should.not.have.a.property("password"); - authUsername.should.equal('pete'); - authPassword.should.equal('secret'); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initialised with bad settings to test else cases',function() { - before(function() { - Users.init({ - type:"foo", - users:{ - username:"fred", - password:'$2a$08$LpYMefvGZ3MjAfZGzcoyR.1BcfHh4wy4NpbN.cEny5aHnWOqjKOXK', - permissions:"*" - } - }); - }); - describe('#get',function() { - it('should fail to return user fred',function(done) { - Users.get("fred").then(function(userf) { - try { - should.not.exist(userf); - done(); - } catch(err) { - done(err); - } - }); - }); - }); - }); - - describe('Initialised with default set as function',function() { - before(function() { - Users.init({ - type:"credentials", - default: function() { return("Done"); } - }); - }); - after(function() { - Users.init({}); - }); - describe('#default',function() { - it('handles api.default being a function',function(done) { - Users.should.have.property('default').which.is.a.Function(); - (Users.default()).should.equal("Done"); - done(); - }); - }); - }); - - describe('Initialised with tokens set as function',function() { - before(function() { - Users.init({ - type:"strategy", - tokens: function(token) { return("Done-"+token); } - }); - }); - after(function() { - Users.init({}); - }); - describe('#tokens',function() { - it('handles api.tokens being a function',function(done) { - Users.should.have.property('tokens').which.is.a.Function(); - (Users.tokens("1234")).should.equal("Done-1234"); - (Users.tokenHeader()).should.equal("authorization"); - done(); - }); - }); - }); - - describe('Initialised with tokens set as function and tokenHeader set as token header name',function() { - before(function() { - Users.init({ - type:"strategy", - tokens: function(token) { return("Done-"+token); }, - tokenHeader: "X-TEST-TOKEN" - }); - }); - after(function() { - Users.init({}); - }); - describe('#tokens',function() { - it('handles api.tokens being a function and api.tokenHeader being a header name',function(done) { - Users.should.have.property('tokens').which.is.a.Function(); - (Users.tokens("1234")).should.equal("Done-1234"); - Users.should.have.property('tokenHeader').which.is.a.Function(); - (Users.tokenHeader()).should.equal("x-test-token"); - done(); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/comms_spec.js b/test/unit/@node-red/editor-api/lib/editor/comms_spec.js deleted file mode 100644 index 4197dc469..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/comms_spec.js +++ /dev/null @@ -1,618 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -const stoppable = require('stoppable'); - -var http = require('http'); -var express = require('express'); -var app = express(); -var WebSocket = require('ws'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var comms = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/comms"); -var Users = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/users"); -var Tokens = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/tokens"); -var Strategies = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth/strategies"); -var address = '127.0.0.1'; -var listenPort = 0; // use ephemeral port - - -describe("api/editor/comms", function() { - var connections = []; - var mockComms = { - addConnection: function(opts) { - connections.push(opts.client); - return Promise.resolve() - }, - removeConnection: function(opts) { - for (var i=0;i{}); - return p; - } - } - } - }); - }); - - it('returns stored credentials',function(done) { - request(app) - .get("/credentials/known-type/n1") - .expect("Content-Type",/json/) - .expect(200) - .end(function(err,res) { - if (err) { - done(err); - } else { - try { - res.body.should.have.a.property("user1","abc"); - res.body.should.not.have.a.property("password1"); - res.body.should.have.a.property("has_password1",true); - done(); - } catch(e) { - done(e); - } - } - }) - }); - it('returns any error',function(done) { - request(app) - .get("/credentials/unknown-type/n2") - .expect("Content-Type",/json/) - .expect(400) - .end(function(err,res) { - if (err) { - done(err); - } else { - try { - res.body.should.have.property('code'); - res.body.code.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal('message'); - done(); - } catch(e) { - done(e); - } - } - }) - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/index_spec.js b/test/unit/@node-red/editor-api/lib/editor/index_spec.js deleted file mode 100644 index 8ed4d88f3..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/index_spec.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require("supertest"); -var express = require("express"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var editorApi = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor"); -var comms = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/comms"); -var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings"); -var auth = NR_TEST_UTILS.require("@node-red/editor-api/lib/auth"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; - -describe("api/editor/index", function() { - var app; - describe("disabled the editor", function() { - beforeEach(function() { - sinon.stub(comms,'init').callsFake(function(){}); - sinon.stub(info,'init').callsFake(function(){}); - }); - afterEach(function() { - comms.init.restore(); - info.init.restore(); - }); - it("disables the editor", function() { - var editorApp = editorApi.init({},{disableEditor:true},{}); - should.not.exist(editorApp); - comms.init.called.should.be.false(); - info.init.called.should.be.false(); - }); - }); - describe("enables the editor", function() { - var mockList = [ - 'library','theme','locales','credentials','comms',"settings" - ] - var isStarted = true; - var errors = []; - var session_data = {}; - before(function() { - sinon.stub(auth,'needsPermission').callsFake(function(permission) { - return function(req,res,next) { next(); } - }); - mockList.forEach(function(m) { - sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m),"init").callsFake(function(){}); - }); - sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"),"app").callsFake(function(){ return express()}); - }); - after(function() { - mockList.forEach(function(m) { - NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m).init.restore(); - }) - NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme").app.restore(); - auth.needsPermission.restore(); - log.error.restore(); - }); - - before(function() { - sinon.stub(log,"error").callsFake(function(err) { errors.push(err)}) - app = editorApi.init({},{httpNodeRoot:true, httpAdminRoot: true,disableEditor:false,exportNodeSettings:function(){}},{ - isStarted: () => Promise.resolve(isStarted) - }); - }); - it('serves the editor', function(done) { - request(app) - .get("/") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - // Index page should probably mention Node-RED somewhere - res.text.indexOf("Node-RED").should.not.eql(-1); - done(); - }); - }); - it('serves icons', function(done) { - request(app) - .get("/red/images/icons/arrow-in.svg") - .expect(200) - .expect("Content-Type", /image\/svg\+xml/) - .end(function(err,res) { - done(err); - }); - }); - it('handles page not there', function(done) { - request(app) - .get("/foo") - .expect(404,done) - }); - it('warns if runtime not started', function(done) { - isStarted = false; - request(app) - .get("/") - .expect(503) - .end(function(err,res) { - if (err) { - return done(err); - } - res.text.should.eql("Not started"); - errors.should.have.lengthOf(1); - errors[0].should.eql("Node-RED runtime not started"); - done(); - }); - }); - // it.skip('GET /settings', function(done) { - // request(app).get("/settings").expect(200).end(function(err,res) { - // if (err) { - // return done(err); - // } - // // permissionChecks.should.have.property('settings.read',1); - // done(); - // }) - // }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/library_spec.js b/test/unit/@node-red/editor-api/lib/editor/library_spec.js deleted file mode 100644 index 4a2c281b7..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/library_spec.js +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require('body-parser'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var library = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/library"); - -var app; - -describe("api/editor/library", function() { - - before(function() { - app = express(); - app.use(bodyParser.json()); - app.get(/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,library.getEntry); - app.post(/library\/([^\/]+)\/([^\/]+)\/(.*)/,library.saveEntry); - }); - after(function() { - }); - - it('returns an individual entry - flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve('{"a":1,"b":2}'); - } - } - }); - request(app) - .get('/library/local/flows/abc') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('a',1); - res.body.should.have.property('b',2); - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','abc'); - done(); - }); - }) - it('returns a directory listing - flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve({"a":1,"b":2}); - } - } - }); - request(app) - .get('/library/local/flows/abc/def') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('a',1); - res.body.should.have.property('b',2); - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','abc/def'); - done(); - }); - }) - it('returns an individual entry - non-flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve('{"a":1,"b":2}'); - } - } - }); - request(app) - .get('/library/local/non-flow/abc') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','non-flow'); - opts.should.have.property('path','abc'); - res.text.should.eql('{"a":1,"b":2}'); - done(); - }); - }) - it('returns a directory listing - non-flow type', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - return Promise.resolve({"a":1,"b":2}); - } - } - }); - request(app) - .get('/library/local/non-flow/abc/def') - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('a',1); - res.body.should.have.property('b',2); - opts.should.have.property('library','local'); - opts.should.have.property('type','non-flow'); - opts.should.have.property('path','abc/def'); - done(); - }); - }) - - it('returns an error on individual get', function(done) { - var opts; - library.init({ - library: { - getEntry: function(_opts) { - opts = _opts; - var err = new Error("message"); - err.code = "random_error"; - err.status = 400; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .get('/library/local/flows/123') - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','123'); - - res.body.should.have.property('code'); - res.body.code.should.be.equal("random_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("message"); - done(); - }); - }); - - - it('saves an individual entry - flow type', function(done) { - var opts; - library.init({ - library: { - saveEntry: function(_opts) { - opts = _opts; - return Promise.resolve(); - } - } - }); - request(app) - .post('/library/local/flows/abc/def') - .expect(204) - .send({a:1,b:2,c:3}) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','flows'); - opts.should.have.property('path','abc/def'); - opts.should.have.property('meta',{}); - opts.should.have.property('body',JSON.stringify({a:1,b:2,c:3})); - done(); - }); - }) - - it('saves an individual entry - non-flow type', function(done) { - var opts; - library.init({ - library: { - saveEntry: function(_opts) { - opts = _opts; - return Promise.resolve(); - } - } - }); - request(app) - .post('/library/local/non-flow/abc/def') - .expect(204) - .send({a:1,b:2,text:"123"}) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('library','local'); - opts.should.have.property('type','non-flow'); - opts.should.have.property('path','abc/def'); - opts.should.have.property('meta',{a:1,b:2}); - opts.should.have.property('body',"123"); - done(); - }); - }) - - it('returns an error on individual save', function(done) { - var opts; - library.init({ - library: { - saveEntry: function(_opts) { - opts = _opts; - var err = new Error("message"); - err.code = "random_error"; - err.status = 400; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - } - }); - request(app) - .post('/library/local/non-flow/abc/def') - .send({a:1,b:2,text:"123"}) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - opts.should.have.property('type','non-flow'); - opts.should.have.property('library','local'); - opts.should.have.property('path','abc/def'); - - res.body.should.have.property('code'); - res.body.code.should.be.equal("random_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("message"); - done(); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/locales_spec.js b/test/unit/@node-red/editor-api/lib/editor/locales_spec.js deleted file mode 100644 index fef686a78..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/locales_spec.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var sinon = require('sinon'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var locales = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/locales"); -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("api/editor/locales", function() { - beforeEach(function() { - }) - afterEach(function() { - }) - describe('get named resource catalog',function() { - var app; - before(function() { - // locales.init({ - // i18n: { - // i: { - // language: function() { return 'en-US'}, - // changeLanguage: function(lang,callback) { - // if (callback) { - // callback(); - // } - // }, - // getResourceBundle: function(lang, namespace) { - // return {namespace:namespace, lang:lang}; - // } - // }, - // } - // }); - locales.init({}); - - // bit of a mess of internal workings - sinon.stub(i18n.i,'changeLanguage').callsFake(function(lang,callback) { if (callback) {callback();}}); - if (i18n.i.getResourceBundle) { - sinon.stub(i18n.i,'getResourceBundle').callsFake(function(lang, namespace) {return {namespace:namespace, lang:lang};}); - } else { - // If i18n.init has not been called, then getResourceBundle isn't - // defined - so hardcode a stub - i18n.i.getResourceBundle = function(lang, namespace) {return {namespace:namespace, lang:lang};}; - i18n.i.getResourceBundle.restore = function() { delete i18n.i.getResourceBundle }; - } - app = express(); - app.get(/locales\/(.+)\/?$/,locales.get); - }); - after(function() { - i18n.i.changeLanguage.restore(); - i18n.i.getResourceBundle.restore(); - }) - it('returns with default language', function(done) { - request(app) - .get("/locales/message-catalog") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('namespace','message-catalog'); - done(); - }); - }); - it('returns with selected language', function(done) { - request(app) - .get("/locales/message-catalog?lng=fr-FR") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('namespace','message-catalog'); - res.body.should.have.property('lang','fr-FR'); - done(); - }); - }); - - it('returns for locale defined only with primary tag ', function(done) { - var orig = i18n.i.getResourceBundle; - i18n.i.getResourceBundle = function (lang, ns) { - if (lang === "ja-JP") { - return undefined; - } - return orig(lang, ns); - }; - request(app) - // returns `ja` instead of `ja-JP` - .get("/locales/message-catalog?lng=ja-JP") - .expect(200) - .end(function(err,res) { - i18n.i.getResourceBundle = orig; - if (err) { - return done(err); - } - res.body.should.have.property('namespace','message-catalog'); - res.body.should.have.property('lang','ja'); - done(); - }); - }); - - }); - - // describe('get all node resource catalogs',function() { - // var app; - // before(function() { - // // bit of a mess of internal workings - // sinon.stub(i18n,'catalog').callsFake(function(namespace, lang) { - // return { - // "node-red": "should not return", - // "test-module-a-id": "test-module-a-catalog", - // "test-module-b-id": "test-module-b-catalog", - // "test-module-c-id": "test-module-c-catalog" - // }[namespace] - // }); - // locales.init({ - // nodes: { - // getNodeList: function(opts) { - // return Promise.resolve([ - // {module:"node-red",id:"node-red-id"}, - // {module:"test-module-a",id:"test-module-a-id"}, - // {module:"test-module-b",id:"test-module-b-id"} - // ]); - // } - // } - // }); - // app = express(); - // app.get("/locales/nodes",locales.getAllNodes); - // }); - // after(function() { - // i18n.catalog.restore(); - // }) - // it('returns with the node catalogs', function(done) { - // request(app) - // .get("/locales/nodes") - // .expect(200) - // .end(function(err,res) { - // if (err) { - // return done(err); - // } - // res.body.should.eql({ - // 'test-module-a-id': 'test-module-a-catalog', - // 'test-module-b-id': 'test-module-b-catalog' - // }); - // done(); - // }); - // }); - // }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/projects_spec.js b/test/unit/@node-red/editor-api/lib/editor/projects_spec.js deleted file mode 100644 index 52b88d0b8..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/projects_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("api/editor/projects", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/settings_spec.js b/test/unit/@node-red/editor-api/lib/editor/settings_spec.js deleted file mode 100644 index 171dca564..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/settings_spec.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require("body-parser"); -var sinon = require('sinon'); - -var app; - -var NR_TEST_UTILS = require("nr-test-utils"); - -var info = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings"); -var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); - -describe("api/editor/settings", function() { - before(function() { - sinon.stub(theme,"settings").callsFake(function() { return { existing: 123, test: 456 };}); - app = express(); - app.use(bodyParser.json()); - app.get("/settings/user",function(req,res,next) {req.user = "fred"; next()}, info.userSettings); - app.post("/settings/user",function(req,res,next) {req.user = "fred"; next()},info.updateUserSettings); - }); - - after(function() { - theme.settings.restore(); - }); - - it('returns the user settings', function(done) { - info.init({ - settings: { - getUserSettings: function(opts) { - if (opts.user !== "fred") { - return Promise.reject(new Error("Unknown user")); - } - return Promise.resolve({ - c:3, - d:4 - }) - } - } - }); - request(app) - .get("/settings/user") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.eql({c:3,d:4}); - done(); - }); - }); - it('updates the user settings', function(done) { - var update; - info.init({ - settings: { - updateUserSettings: function(opts) { - if (opts.user !== "fred") { - return Promise.reject(new Error("Unknown user")); - } - update = opts.settings; - return Promise.resolve() - } - } - }); - request(app) - .post("/settings/user") - .send({ - e:4, - f:5 - }) - .expect(204) - .end(function(err,res) { - if (err) { - return done(err); - } - update.should.eql({e:4,f:5}); - done(); - }); - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js b/test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js deleted file mode 100644 index 1647cd99d..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/sshkeys_spec.js +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require("supertest"); -var express = require("express"); -var NR_TEST_UTILS = require("nr-test-utils"); -var sshkeys = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/sshkeys"); -var bodyParser = require("body-parser"); - - -describe("api/editor/sshkeys", function() { - var app; - var mockRuntime = { - settings: { - getUserKeys: function() {}, - getUserKey: function() {}, - generateUserKey: function() {}, - removeUserKey: function() {} - } - } - before(function() { - sshkeys.init(mockRuntime); - app = express(); - app.use(bodyParser.json()); - app.use("/settings/user/keys", sshkeys.app()); - }); - - beforeEach(function() { - sinon.stub(mockRuntime.settings, "getUserKeys"); - sinon.stub(mockRuntime.settings, "getUserKey"); - sinon.stub(mockRuntime.settings, "generateUserKey"); - sinon.stub(mockRuntime.settings, "removeUserKey"); - }) - afterEach(function() { - mockRuntime.settings.getUserKeys.restore(); - mockRuntime.settings.getUserKey.restore(); - mockRuntime.settings.generateUserKey.restore(); - mockRuntime.settings.removeUserKey.restore(); - }) - - it('GET /settings/user/keys --- return empty list', function(done) { - mockRuntime.settings.getUserKeys.returns(Promise.resolve([])); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - res.body.keys.should.be.empty(); - done(); - }); - }); - - it('GET /settings/user/keys --- return normal list', function(done) { - var fileList = [ - 'test_key01', - 'test_key02' - ]; - var retList = fileList.map(function(elem) { - return { - name: elem - }; - }); - mockRuntime.settings.getUserKeys.returns(Promise.resolve(retList)); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - for (var item of retList) { - res.body.keys.should.containEql(item); - } - done(); - }); - }); - - it('GET /settings/user/keys --- return Error', function(done) { - var errInstance = new Error("Messages here....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKeys.returns(p); - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return 404', function(done) { - var errInstance = new Error("Not Found."); - errInstance.code = "not_found"; - errInstance.status = 404; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKey.returns(p); - request(app) - .get("/settings/user/keys/NOT_REAL") - .expect(404) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - it('GET /settings/user/keys --- return Unexpected Error', function(done) { - var errInstance = new Error(); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKeys.returns(p) - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return content', function(done) { - var key_file_name = "test_key"; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - mockRuntime.settings.getUserKey.returns(Promise.resolve(fileContent)); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - mockRuntime.settings.getUserKey.called.should.be.true(); - mockRuntime.settings.getUserKey.firstCall.args[0].should.eql({ user: undefined, id: 'test_key' }); - res.body.should.be.deepEqual({ publickey: fileContent }); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.getUserKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("Messages....."); - done(); - }); - }); - - it('POST /settings/user/keys --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.settings.generateUserKey.returns(Promise.resolve(key_file_name)); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - - it('POST /settings/user/keys --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.generateUserKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('POST /settings/user/keys --- return Unexpected error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.generateUserKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("Messages....."); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.settings.removeUserKey.returns(Promise.resolve(true)); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(204) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.be.deepEqual({}); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.removeUserKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.settings.removeUserKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('code'); - res.body.code.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal('Messages.....'); - done(); - }); - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/theme_spec.js b/test/unit/@node-red/editor-api/lib/editor/theme_spec.js deleted file mode 100644 index 900be126f..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/theme_spec.js +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require("supertest"); -var express = require('express'); -var sinon = require('sinon'); -var fs = require("fs"); - -var app = express(); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var theme = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"); - -describe("api/editor/theme", function () { - beforeEach(function () { - sinon.stub(fs, "statSync").callsFake(function () { return true; }); - }); - afterEach(function () { - theme.init({settings: {}}); - fs.statSync.restore(); - }); - it("applies the default theme", async function () { - var result = theme.init({}); - should.not.exist(result); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("title", "Node-RED"); - context.page.should.have.a.property("favicon", "favicon.ico"); - context.page.should.have.a.property("tabicon"); - context.page.tabicon.should.have.a.property("icon", "red/images/node-red-icon-black.svg"); - context.page.tabicon.should.have.a.property("colour", "#8f0000"); - context.should.have.a.property("header"); - context.header.should.have.a.property("title", "Node-RED"); - context.header.should.have.a.property("image", "red/images/node-red.svg"); - context.should.have.a.property("asset"); - context.asset.should.have.a.property("red", "red/red.min.js"); - context.asset.should.have.a.property("main", "red/main.min.js"); - context.asset.should.have.a.property("vendorMonaco", ""); - - should.not.exist(theme.settings()); - }); - - it("uses non-minified js files when in dev mode", async function () { - const previousEnv = process.env.NODE_ENV; - try { - process.env.NODE_ENV = 'development' - theme.init({}); - var context = await theme.context(); - context.asset.should.have.a.property("red", "red/red.js"); - context.asset.should.have.a.property("main", "red/main.js"); - } finally { - process.env.NODE_ENV = previousEnv; - } - }); - - it("Adds monaco bootstrap when enabled", async function () { - theme.init({ - editorTheme: { - codeEditor: { - lib: 'monaco' - } - } - }); - var context = await theme.context(); - context.asset.should.have.a.property("vendorMonaco", "vendor/monaco/monaco-bootstrap.js"); - }); - - it("picks up custom theme", async function () { - theme.init({ - editorTheme: { - page: { - title: "Test Page Title", - favicon: "/absolute/path/to/theme/favicon", - tabicon: { - icon: "/absolute/path/to/theme/tabicon", - colour: "#8f008f" - }, - css: [ - "/absolute/path/to/custom/css/file.css" - ], - scripts: "/absolute/path/to/script.js" - }, - header: { - title: "Test Header Title", - url: "http://nodered.org", - image: "/absolute/path/to/header/image" // or null to remove image - }, - - deployButton: { - type: "simple", - label: "Save", - icon: "/absolute/path/to/deploy/button/image" // or null to remove image - }, - - menu: { // Hide unwanted menu items by id. see editor/js/main.js:loadEditor for complete list - "menu-item-import-library": false, - "menu-item-export-library": false, - "menu-item-keyboard-shortcuts": false, - "menu-item-help": { - label: "Alternative Help Link Text", - url: "http://example.com" - } - }, - - userMenu: false, // Hide the user-menu even if adminAuth is enabled - - login: { - image: "/absolute/path/to/login/page/big/image" // a 256x256 image - }, - - palette: { - editable: true, - catalogues: ['https://catalogue.nodered.org/catalogue.json'], - theme: [{ category: ".*", type: ".*", color: "#f0f" }] - }, - - projects: { - enabled: false - } - } - }); - - theme.app(); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("title", "Test Page Title"); - context.page.should.have.a.property("favicon", "theme/favicon/favicon"); - context.page.should.have.a.property("tabicon") - context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon"); - context.page.tabicon.should.have.a.property("colour", "#8f008f") - context.should.have.a.property("header"); - context.header.should.have.a.property("title", "Test Header Title"); - context.header.should.have.a.property("url", "http://nodered.org"); - context.header.should.have.a.property("image", "theme/header/image"); - context.page.should.have.a.property("css"); - context.page.css.should.have.lengthOf(1); - context.page.css[0].should.eql('theme/css/file.css'); - context.page.should.have.a.property("scripts"); - context.page.scripts.should.have.lengthOf(1); - context.page.scripts[0].should.eql('theme/scripts/script.js'); - context.should.have.a.property("login"); - context.login.should.have.a.property("image", "theme/login/image"); - - var settings = theme.settings(); - settings.should.have.a.property("deployButton"); - settings.deployButton.should.have.a.property("type", "simple"); - settings.deployButton.should.have.a.property("label", "Save"); - settings.deployButton.should.have.a.property("icon", "theme/deploy/image"); - settings.should.have.a.property("userMenu"); - settings.userMenu.should.be.eql(false); - settings.should.have.a.property("menu"); - settings.menu.should.have.a.property("menu-item-import-library", false); - settings.menu.should.have.a.property("menu-item-export-library", false); - settings.menu.should.have.a.property("menu-item-keyboard-shortcuts", false); - settings.menu.should.have.a.property("menu-item-help", { label: "Alternative Help Link Text", url: "http://example.com" }); - settings.should.have.a.property("palette"); - settings.palette.should.have.a.property("editable", true); - settings.palette.should.have.a.property("catalogues", ['https://catalogue.nodered.org/catalogue.json']); - settings.palette.should.have.a.property("theme", [{ category: ".*", type: ".*", color: "#f0f" }]); - settings.should.have.a.property("projects"); - settings.projects.should.have.a.property("enabled", false); - }); - - it("picks up backwards compatible tabicon setting", async function () { - theme.init({ - editorTheme: { - page: { - tabicon: "/absolute/path/to/theme/tabicon", - } - } - }); - - theme.app(); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("tabicon"); - context.page.tabicon.should.have.a.property("icon", "theme/tabicon/tabicon"); - // The colour property should remain as default in this case as the - // legacy format for defining tabicon doesn't allow specifying a colour - context.page.tabicon.should.have.a.property("colour", "#8f0000"); - - }); - - it("test explicit userMenu set to true in theme setting", function () { - theme.init({ - editorTheme: { - userMenu: true, - } - }); - - theme.app(); - - var settings = theme.settings(); - settings.should.have.a.property("userMenu"); - settings.userMenu.should.be.eql(true); - - }); - - - it("includes list of plugin themes", function(done) { - theme.init({},{ - plugins: { getPluginsByType: _ => [{id:"theme-plugin"}] } - }); - const app = theme.app(); - request(app) - .get("/") - .end(function(err,res) { - if (err) { - return done(err); - } - try { - const response = JSON.parse(res.text); - response.should.have.property("themes"); - response.themes.should.eql(["theme-plugin"]) - done(); - } catch(err) { - done(err); - } - }); - }); - - it("includes theme plugin settings", async function () { - theme.init({ - editorTheme: { - theme: 'test-theme' - } - },{ - plugins: { getPlugin: t => { - return ({'test-theme':{ - path: '/abosolute/path/to/plugin', - css: [ - "path/to/custom/css/file1.css", - "/invalid/path/to/file2.css", - "../another/invalid/path/file3.css" - ], - scripts: [ - "path/to/custom/js/file1.js", - "/invalid/path/to/file2.js", - "../another/invalid/path/file3.js" - ] - }})[t.id]; - } } - }); - - theme.app(); - - var context = await theme.context(); - context.should.have.a.property("page"); - context.page.should.have.a.property("css"); - context.page.css.should.have.lengthOf(1); - context.page.css[0].should.eql('theme/css/file1.css'); - context.page.should.have.a.property("scripts"); - context.page.scripts.should.have.lengthOf(1); - context.page.scripts[0].should.eql('theme/scripts/file1.js'); - - }); -}); diff --git a/test/unit/@node-red/editor-api/lib/editor/ui_spec.js b/test/unit/@node-red/editor-api/lib/editor/ui_spec.js deleted file mode 100644 index 0380adcde..000000000 --- a/test/unit/@node-red/editor-api/lib/editor/ui_spec.js +++ /dev/null @@ -1,210 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var request = require("supertest"); -var express = require("express"); -var fs = require("fs"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var ui = NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/ui"); - - -describe("api/editor/ui", function() { - var app; - - before(function() { - ui.init({ - nodes: { - getIcon: function(opts) { - return new Promise(function(resolve,reject) { - if (opts.icon === "icon.png") { - fs.readFile(NR_TEST_UTILS.resolve("@node-red/editor-client/src/images/icons/arrow-in.svg"), function(err,data) { - resolve(data); - }) - } else { - resolve(null); - } - }); - }, - getModuleResource: async function(opts) { - if (opts.module !== "test-module" || opts.path !== "a/path/text.txt") { - return null; - } else { - return "Some text data"; - } - } - } - }); - }); - describe("slash handler", function() { - before(function() { - app = express(); - app.get("/foo",ui.ensureSlash,function(req,res) { res.sendStatus(200);}); - }); - it('redirects if the path does not end in a slash',function(done) { - request(app) - .get('/foo') - .expect(301,done); - }); - it('redirects if the path, with query string, does not end in a slash',function(done) { - request(app) - .get('/foo?abc=def') - .expect(301) - .end(function(err,res) { - if (err) { - return done(err); - } - res.header['location'].should.equal("/foo/?abc=def"); - done(); - }); - }); - - it('does not redirect if the path ends in a slash',function(done) { - request(app) - .get('/foo/') - .expect(200,done); - }); - }); - - describe("icon handler", function() { - before(function() { - app = express(); - app.get("/icons/:module/:icon",ui.icon); - }); - - function binaryParser(res, callback) { - res.setEncoding('binary'); - res.data = ''; - res.on('data', function (chunk) { - res.data += chunk; - }); - res.on('end', function () { - callback(null, Buffer.from(res.data, 'binary')); - }); - } - function compareBuffers(b1,b2) { - b1.length.should.equal(b2.length); - for (var i=0;i layer.name === 'testMiddleware') - should(middlewareFound).be.empty(); - done(); - }); - - it('only accepts functions as middleware',function(done) { - const testMiddleware = function(req, res, next){ next(); }; - api.init({ httpAdminRoot: true, httpAdminMiddleware: testMiddleware },{},{},{}); - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware') - should(middlewareFound).be.length(1); - done(); - }); - }); - - describe('initialises api with authentication enabled', function(done) { - - it('enables an oauth/openID based authentication mechanism',function(done) { - const stub = sinon.stub(apiAuth, 'genericStrategy').callsFake(function(){}); - const adminAuth = { type: 'strategy', strategy: {} } - api.init({ httpAdminRoot: true, adminAuth },{},{},{}); - should(stub.called).be.ok(); - stub.restore(); - done(); - }); - - it('enables password protection',function(done) { - const adminAuth = { type: 'credentials' } - api.init({ httpAdminRoot: true, adminAuth },{},{},{}); - - // is the name ("initialize") of the passport middleware present - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'initialize') - should(middlewareFound).be.length(1); - done(); - }); - - }); - - describe('initialises api with custom cors config', function (done) { - const httpAdminCors = { - origin: "*", - methods: "GET,PUT,POST,DELETE" - }; - - it('uses default cors middleware when user settings absent', function(done){ - api.init({ httpAdminRoot: true }, {}, {}, {}); - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'corsMiddleware') - should(middlewareFound).be.length(1); - done(); - }) - - it('enables custom cors middleware when settings present', function(done){ - api.init({ httpAdminRoot: true, httpAdminCors }, {}, {}, {}); - const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'corsMiddleware') - should(middlewareFound).be.length(2); - done(); - }) - }); - - describe('editor start', function (done) { - - it('cannot be started when editor is disabled', function (done) { - const stub = sinon.stub(apiEditor, 'start').callsFake(function () { - return Promise.resolve(true); - }); - api.init({ httpAdminRoot: true, disableEditor: true }, {}, {}, {}); - should(api.start()).resolvedWith(true); - stub.restore(); - done(); - }); - - it('can be started when editor enabled', function (done) { - const stub = sinon.stub(apiEditor, 'start'); - api.init({ httpAdminRoot: true, disableEditor: false }, {}, {}, {}); - api.start(); - should(stub.called).be.true(); - stub.restore(); - done(); - }); - - }); - -}); diff --git a/test/unit/@node-red/editor-api/lib/util_spec.js b/test/unit/@node-red/editor-api/lib/util_spec.js deleted file mode 100644 index 26cd76be8..000000000 --- a/test/unit/@node-red/editor-api/lib/util_spec.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var request = require('supertest'); -var express = require('express'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var apiUtil = NR_TEST_UTILS.require("@node-red/editor-api/lib/util"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("api/util", function() { - describe("errorHandler", function() { - var loggedError = null; - var loggedEvent = null; - var app; - before(function() { - app = express(); - sinon.stub(log,'error').callsFake(function(msg) {loggedError = msg;}); - sinon.stub(log,'audit').callsFake(function(event) {loggedEvent = event;}); - app.get("/tooLarge", function(req,res) { - var err = new Error(); - err.message = "request entity too large"; - throw err; - },apiUtil.errorHandler) - app.get("/stack", function(req,res) { - var err = new Error(); - err.message = "stacktrace"; - throw err; - },apiUtil.errorHandler) - }); - after(function() { - log.error.restore(); - log.audit.restore(); - }) - beforeEach(function() { - loggedError = null; - loggedEvent = null; - }) - it("logs an error for request entity too large", function(done) { - request(app).get("/tooLarge").expect(400).end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("error","unexpected_error"); - res.body.should.have.property("message","Error: request entity too large"); - - loggedError.should.have.property("message","request entity too large"); - - loggedEvent.should.have.property("event","api.error"); - loggedEvent.should.have.property("error","unexpected_error"); - loggedEvent.should.have.property("message","Error: request entity too large"); - done(); - }); - }) - it("logs an error plus stack for other errors", function(done) { - request(app).get("/stack").expect(400).end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property("error","unexpected_error"); - res.body.should.have.property("message","Error: stacktrace"); - - /Error: stacktrace\s*at.*util_spec.js/m.test(loggedError).should.be.true(); - - loggedEvent.should.have.property("event","api.error"); - loggedEvent.should.have.property("error","unexpected_error"); - loggedEvent.should.have.property("message","Error: stacktrace"); - - - - done(); - }); - }); - }) - - describe('determineLangFromHeaders', function() { - var oldDefaultLang; - before(function() { - oldDefaultLang = i18n.defaultLang; - i18n.defaultLang = "en-US"; - }) - after(function() { - i18n.defaultLang = oldDefaultLang; - }) - it('returns the default lang if non provided', function() { - apiUtil.determineLangFromHeaders(null).should.eql("en-US"); - }) - it('returns the first language accepted', function() { - apiUtil.determineLangFromHeaders(['fr-FR','en-GB']).should.eql("fr-FR"); - }) - }) -}); diff --git a/test/unit/@node-red/registry/lib/deprecated_spec.js b/test/unit/@node-red/registry/lib/deprecated_spec.js deleted file mode 100644 index b9c35dd31..000000000 --- a/test/unit/@node-red/registry/lib/deprecated_spec.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var deprecated = NR_TEST_UTILS.require("@node-red/registry/lib/deprecated.js"); - -describe('deprecated', function() { - it('should return info on a node',function() { - deprecated.get("irc in").should.eql({module:"node-red-node-irc"}); - }); - it('should return null for non-deprecated node',function() { - should.not.exist(deprecated.get("foo")); - }); -}); diff --git a/test/unit/@node-red/registry/lib/externalModules_spec.js b/test/unit/@node-red/registry/lib/externalModules_spec.js deleted file mode 100644 index 2158f93f7..000000000 --- a/test/unit/@node-red/registry/lib/externalModules_spec.js +++ /dev/null @@ -1,371 +0,0 @@ - // init: init, - // register: register, - // registerSubflow: registerSubflow, - // checkFlowDependencies: checkFlowDependencies, - // require: requireModule - // - -const should = require("should"); -const sinon = require("sinon"); -const fs = require("fs-extra"); -const path = require("path"); -const os = require("os"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const externalModules = NR_TEST_UTILS.require("@node-red/registry/lib/externalModules"); -const exec = NR_TEST_UTILS.require("@node-red/util/lib/exec"); -const hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); - -let homeDir; - -async function createUserDir() { - if (!homeDir) { - homeDir = path.join(os.tmpdir(),"nr-test-"+Math.floor(Math.random()*100000)); - } - await fs.ensureDir(homeDir); -} - -async function setupExternalModulesPackage(dependencies) { - await fs.writeFile(path.join(homeDir,"package.json"),`{ -"name": "Node-RED-External-Modules", -"description": "These modules are automatically installed by Node-RED to use in Function nodes.", -"version": "1.0.0", -"private": true, -"dependencies": ${JSON.stringify(dependencies)} -}`) -} - -describe("externalModules api", function() { - beforeEach(async function() { - await createUserDir() - }) - afterEach(async function() { - hooks.clear(); - await fs.remove(homeDir); - }) - describe("checkFlowDependencies", function() { - beforeEach(function() { - sinon.stub(exec,"run").callsFake(async function(cmd, args, options) { - let error; - let moduleName = args[args.length-1]; - if (moduleName === "moduleNotFound") { - error = new Error(); - error.stderr = "E404"; - } else if (moduleName === "moduleVersionNotFound") { - error = new Error(); - error.stderr = "ETARGET"; - } else if (moduleName === "moduleFail") { - error = new Error(); - error.stderr = "Some unexpected install error"; - } - if (error) { - throw error; - } - }) - }) - afterEach(function() { - exec.run.restore(); - }) - it("does nothing when no types are registered",async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.false(); - }); - - it("skips install for modules already installed", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - await setupExternalModulesPackage({"foo": "1.2.3", "bar":"2.3.4"}); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.false(); - }) - - it("skips install for built-in modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "fs"}]} - ]) - exec.run.called.should.be.false(); - }) - - it("installs missing modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.true(); - }) - - - it("calls pre/postInstall hooks", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - let receivedPreEvent,receivedPostEvent; - hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; }) - hooks.add("postInstall", function(event) { receivedPostEvent = event; }) - - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.true(); - // exec.run.lastCall.args[1].should.eql([ 'install', 'a', 'foo' ]); - receivedPreEvent.should.have.property("module","foo") - receivedPreEvent.should.have.property("version") - receivedPreEvent.should.have.property("dir") - receivedPreEvent.should.eql(receivedPostEvent) - }) - - it("skips npm install if preInstall returns false", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - let receivedPreEvent,receivedPostEvent; - hooks.add("preInstall", function(event) { receivedPreEvent = event; return false }) - hooks.add("postInstall", function(event) { receivedPostEvent = event; }) - - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - exec.run.called.should.be.false(); - receivedPreEvent.should.have.property("module","foo") - receivedPreEvent.should.have.property("version") - receivedPreEvent.should.have.property("dir") - receivedPreEvent.should.eql(receivedPostEvent) - }) - - - it("installs missing modules from inside subflow module", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - externalModules.registerSubflow("sf", {"flow":[{type: "function", libs:[{module: "foo"}]}]}); - await externalModules.checkFlowDependencies([ - {type: "sf"} - ]) - exec.run.called.should.be.true(); - }) - - it("reports install fail - 404", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleNotFound"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleNotFound"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code",404); - - } - }) - it("reports install fail - target", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleVersionNotFound"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleVersionNotFound"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code",404); - } - }) - - it("reports install fail - unexpected", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleFail"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleFail"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","unexpected_error"); - } - }) - it("reports install fail - multiple", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "moduleNotFound"},{module: "moduleFail"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.called.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(2); - // Sort the array so we know the order to test for - err.sort(function(A,B) { - return A.module.module.localeCompare(B.module.module); - }) - err[1].should.have.property("module"); - err[1].module.should.have.property("module","moduleNotFound"); - err[1].should.have.property("error"); - err[1].error.should.have.property("code",404); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","moduleFail"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","unexpected_error"); - - } - }) - it("reports install fail - install disabled", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - allowInstall: false - } - }}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - {type: "function", libs:[{module: "foo"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - // Should not try to install - exec.run.called.should.be.false(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","foo"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","install_not_allowed"); - } - }) - - it("reports install fail - module disallowed", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['foo'] - } - }}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - // foo disallowed - // bar allowed - {type: "function", libs:[{module: "foo"},{module: "bar"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.calledOnce.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","foo"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","install_not_allowed"); - } - }) - - it("reports install fail - built-in module disallowed", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['fs'] - } - }}); - externalModules.register("function", "libs"); - try { - await externalModules.checkFlowDependencies([ - // foo disallowed - // bar allowed - {type: "function", libs:[{module: "fs"},{module: "bar"}]} - ]) - throw new Error("checkFlowDependencies did not reject after install fail") - } catch(err) { - exec.run.calledOnce.should.be.true(); - Array.isArray(err).should.be.true(); - err.should.have.length(1); - err[0].should.have.property("module"); - err[0].module.should.have.property("module","fs"); - err[0].should.have.property("error"); - err[0].error.should.have.property("code","module_not_allowed"); - } - }) - }) - describe("require", async function() { - it("requires built-in modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - const result = externalModules.require("fs") - result.should.eql(require("fs")); - }) - it("rejects unknown modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - try { - externalModules.require("foo") - throw new Error("require did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - - it("rejects disallowed modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['fs'] - } - }}); - try { - externalModules.require("fs") - throw new Error("require did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - }) - describe("import", async function() { - it("import built-in modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - const result = await externalModules.import("fs") - // `result` won't have the `should` property - should.exist(result); - should.exist(result.existsSync); - }) - it("rejects unknown modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}}); - try { - await externalModules.import("foo") - throw new Error("import did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - - it("rejects disallowed modules", async function() { - externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: { - modules: { - denyList: ['fs'] - } - }}); - try { - await externalModules.import("fs") - throw new Error("import did not reject after fail") - } catch(err) { - err.should.have.property("code","module_not_allowed"); - } - }) - }) -}); diff --git a/test/unit/@node-red/registry/lib/index_spec.js b/test/unit/@node-red/registry/lib/index_spec.js deleted file mode 100644 index db0629225..000000000 --- a/test/unit/@node-red/registry/lib/index_spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var fs = require("fs"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var registry = NR_TEST_UTILS.require("@node-red/registry"); - -var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer"); -var loader = NR_TEST_UTILS.require("@node-red/registry/lib/loader"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); - -describe('red/registry/index', function() { - var stubs = []; - afterEach(function() { - while(stubs.length) { - stubs.pop().restore(); - } - }) - describe('#init',function() { - it('intialises components', function() { - stubs.push(sinon.stub(installer,"init")); - stubs.push(sinon.stub(loader,"init")); - stubs.push(sinon.stub(typeRegistry,"init")); - - registry.init({settings:{}}); - installer.init.called.should.be.true(); - loader.init.called.should.be.true(); - typeRegistry.init.called.should.be.true(); - }) - }); - - describe('#addModule', function() { - it('loads the module and returns its info', function(done) { - stubs.push(sinon.stub(loader,"addModule").callsFake(function(module) { - return Promise.resolve(); - })); - stubs.push(sinon.stub(typeRegistry,"getModuleInfo").callsFake(function(module) { - return "info"; - })); - registry.addModule("foo").then(function(info) { - info.should.eql("info"); - done(); - }).catch(function(err) { done(err); }); - }); - it('rejects if loader rejects', function(done) { - stubs.push(sinon.stub(loader,"addModule").callsFake(function(module) { - return Promise.reject("error"); - })); - stubs.push(sinon.stub(typeRegistry,"getModuleInfo").callsFake(function(module) { - return "info"; - })); - registry.addModule("foo").then(function(info) { - done(new Error("unexpected resolve")); - }).catch(function(err) { - err.should.eql("error"); - done(); - }) - }); - }); - - describe('#enableNode',function() { - it('enables a node set',function(done) { - stubs.push(sinon.stub(typeRegistry,"enableNodeSet").callsFake(function() { - return Promise.resolve(); - })); - stubs.push(sinon.stub(typeRegistry,"getNodeInfo").callsFake(function() { - return {id:"node-set",loaded:true}; - })); - registry.enableNode("node-set").then(function(ns) { - typeRegistry.enableNodeSet.called.should.be.true(); - ns.should.have.a.property('id','node-set'); - done(); - }).catch(function(err) { done(err); }); - }); - - it('rejects if node unknown',function() { - stubs.push(sinon.stub(typeRegistry,"enableNodeSet").callsFake(function() { - throw new Error('failure'); - })); - /*jshint immed: false */ - (function(){ - registry.enableNode("node-set") - }).should.throw(); - }); - - it('triggers a node load',function(done) { - stubs.push(sinon.stub(typeRegistry,"enableNodeSet").callsFake(function() { - return Promise.resolve(); - })); - var calls = 0; - stubs.push(sinon.stub(typeRegistry,"getNodeInfo").callsFake(function() { - // loaded=false on first call, true on subsequent - return {id:"node-set",loaded:(calls++>0)}; - })); - stubs.push(sinon.stub(loader,"loadNodeSet").callsFake(function(){return Promise.resolve();})); - stubs.push(sinon.stub(typeRegistry,"getFullNodeInfo")); - - registry.enableNode("node-set").then(function(ns) { - typeRegistry.enableNodeSet.called.should.be.true(); - loader.loadNodeSet.called.should.be.true(); - ns.should.have.a.property('id','node-set'); - ns.should.have.a.property('loaded',true); - done(); - }).catch(function(err) { done(err); }); - }); - - }); - -}); diff --git a/test/unit/@node-red/registry/lib/installer_spec.js b/test/unit/@node-red/registry/lib/installer_spec.js deleted file mode 100644 index 80b96ecfb..000000000 --- a/test/unit/@node-red/registry/lib/installer_spec.js +++ /dev/null @@ -1,528 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var fs = require('fs-extra'); -var EventEmitter = require('events'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var installer = NR_TEST_UTILS.require("@node-red/registry/lib/installer"); -var registry = NR_TEST_UTILS.require("@node-red/registry/lib/index"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); -const { events, exec, log, hooks } = NR_TEST_UTILS.require("@node-red/util"); - -describe('nodes/registry/installer', function() { - - var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - _: function(msg) { return msg } - } - - var execResponse; - - beforeEach(function() { - sinon.stub(exec,"run").callsFake(() => execResponse || Promise.resolve("")) - installer.init({}) - }); - - afterEach(function() { - execResponse = null; - if (registry.addModule.restore) { - registry.addModule.restore(); - } - if (registry.removeModule.restore) { - registry.removeModule.restore(); - } - if (typeRegistry.removeModule.restore) { - typeRegistry.removeModule.restore(); - } - if (registry.getModuleInfo.restore) { - registry.getModuleInfo.restore(); - } - if (typeRegistry.getModuleInfo.restore) { - typeRegistry.getModuleInfo.restore(); - } - if (typeRegistry.setModulePendingUpdated.restore) { - typeRegistry.setModulePendingUpdated.restore(); - } - if (fs.statSync.restore) { - fs.statSync.restore(); - } - exec.run.restore(); - hooks.clear(); - }); - - describe("installs module", function() { - it("rejects module name that includes version", function(done) { - installer.installModule("module@version",null,null).catch(function(err) { - err.code.should.be.eql('invalid_module_name'); - done(); - }).catch(done); - }); - it("rejects missing module name", function(done) { - installer.installModule("",null,null).catch(function(err) { - err.code.should.be.eql('invalid_module_name'); - done(); - }).catch(done); - }); - it("rejects null module name", function(done) { - installer.installModule(null,null,null).catch(function(err) { - err.code.should.be.eql('invalid_module_name'); - done(); - }).catch(done); - }); - it("rejects invalid url", function(done) { - installer.installModule("module",null,"abc").catch(function(err) { - err.code.should.be.eql('invalid_module_url'); - done(); - }); - }); - it("rejects when npm returns a 404", function(done) { - var res = { - code: 1, - stdout:"", - stderr:" 404 this_wont_exist" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - installer.installModule("this_wont_exist").catch(function(err) { - err.should.have.property("code",404); - done(); - }).catch(done); - }); - it("rejects when npm does not find specified version", function(done) { - var res = { - code: 1, - stdout:"", - stderr:" version not found: this_wont_exist@0.1.2" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql(404); - done(); - }).catch(done); - }); - it("rejects when update requested to existing version", function(done) { - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.1").catch(function(err) { - err.code.should.be.eql('module_already_loaded'); - done(); - }).catch(done); - }); - it("rejects when update requested to existing version and url", function(done) { - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.1","https://example/foo-0.1.1.tgz").catch(function(err) { - err.code.should.be.eql('module_already_loaded'); - done(); - }).catch(done); - }); - it("rejects with generic error", function(done) { - var res = { - code: 1, - stdout:"", - stderr:" kaboom!" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - installer.installModule("this_wont_exist").then(function() { - done(new Error("Unexpected success")); - }).catch(err => { - // Expected result - done() - }); - }); - it("succeeds when module is found", function(done) { - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist").then(function(info) { - info.should.eql(nodeInfo); - // commsMessages.should.have.length(1); - // commsMessages[0].topic.should.equal("node/added"); - // commsMessages[0].msg.should.eql(nodeInfo.nodes); - done(); - }).catch(done); - }); - it("rejects when non-existant path is provided", function(done) { - this.timeout(20000); - var resourcesDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule","node_modules","NonExistant")); - installer.installModule(resourcesDir).then(function() { - done(new Error("Unexpected success")); - }).catch(function(err) { - if (err.hasOwnProperty("code")) { - err.code.should.eql(404); - done(); - } - else { - console.log("ERRROR::"+err.toString()+"::"); - err.toString().should.eql("Error: Install failed"); - done(); - } - }); - }); - it("succeeds when path is valid node-red module", function(done) { - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - var resourcesDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule","node_modules","TestNodeModule")); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - installer.installModule(resourcesDir).then(function(info) { - info.should.eql(nodeInfo); - done(); - }).catch(done); - }); - it("succeeds when url is valid node-red module", function(done) { - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist",null,"https://example/foo-0.1.1.tgz").then(function(info) { - info.should.eql(nodeInfo); - done(); - }).catch(done); - }); - - it("triggers preInstall and postInstall hooks", function(done) { - let receivedPreEvent,receivedPostEvent; - hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; }) - hooks.add("postInstall", function(event) { receivedPostEvent = event; }) - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - var res = {code: 0,stdout:"",stderr:""} - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","1.2.3").then(function(info) { - exec.run.called.should.be.true(); - exec.run.lastCall.args[1].should.eql([ 'install', 'a', 'this_wont_exist@1.2.3' ]); - info.should.eql(nodeInfo); - should.exist(receivedPreEvent) - receivedPreEvent.should.have.property("module","this_wont_exist") - receivedPreEvent.should.have.property("version","1.2.3") - receivedPreEvent.should.have.property("dir") - receivedPreEvent.should.have.property("url") - receivedPreEvent.should.have.property("isExisting") - receivedPreEvent.should.have.property("isUpgrade") - receivedPreEvent.should.eql(receivedPostEvent) - done(); - }).catch(done); - }); - - it("fails install if preInstall hook fails", function(done) { - let receivedEvent; - hooks.add("preInstall", function(event) { throw new Error("preInstall-error"); }) - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - - installer.installModule("this_wont_exist","1.2.3").catch(function(err) { - exec.run.called.should.be.false(); - done(); - }).catch(done); - }); - - it("skips invoking npm if preInstall returns false", function(done) { - let receivedEvent; - hooks.add("preInstall", function(event) { return false }) - hooks.add("postInstall", function(event) { receivedEvent = event; }) - var nodeInfo = {nodes:{module:"foo",types:["a"]}}; - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","1.2.3").then(function() { - exec.run.called.should.be.false(); - should.exist(receivedEvent); - done(); - }).catch(done); - }); - - it("rollsback install if postInstall hook fails", function(done) { - hooks.add("postInstall", function(event) { throw new Error("fail"); }) - installer.installModule("this_wont_exist","1.2.3").catch(function(err) { - exec.run.calledTwice.should.be.true(); - exec.run.firstCall.args[1].includes("install").should.be.true(); - exec.run.secondCall.args[1].includes("remove").should.be.true(); - done(); - }).catch(done); - }); - - describe("allowUpdate lists", function() { - it("rejects when update requested with allowUpdate set to false", function(done) { - installer.init({ externalModules: { palette: { allowUpdate: false } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql('update_not_allowed'); - done(); - }).catch(done); - }) - it("succeeds when update requested with module not on denyUpdateList", function(done) { - installer.init({ externalModules: { palette: { denyUpdateList: ['this_wont_exist'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_is_allowed",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_is_allowed","0.1.2").then(function() { - done(); - }).catch(done); - }) - it("rejects when update requested with module on denyUpdateList", function(done) { - installer.init({ externalModules: { palette: { denyUpdateList: ['this_wont_exist'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_is_allowed",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql('update_not_allowed'); - done(); - }).catch(done); - }) - it("succeeds when update requested with module on allowUpdateList", function(done) { - installer.init({ externalModules: { palette: { allowUpdateList: ['this_is_allowed'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_is_allowed",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_is_allowed","0.1.2").then(function() { - done(); - }).catch(done); - }) - it("rejects when update requested with module not on allowUpdateList", function(done) { - installer.init({ externalModules: { palette: { allowUpdateList: ['this_is_allowed'] } } }) - sinon.stub(typeRegistry,"getModuleInfo").callsFake(function() { - return { - user: true, - version: "0.1.1" - } - }); - - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - var nodeInfo = {nodes:{module:"this_wont_exist",types:["a"]}}; - - var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - sinon.stub(typeRegistry,"setModulePendingUpdated").callsFake(function() { - return Promise.resolve(nodeInfo); - }); - - installer.installModule("this_wont_exist","0.1.2").catch(function(err) { - err.code.should.be.eql('update_not_allowed'); - done(); - }).catch(done); - }) - }); - }); - describe("uninstalls module", function() { - it("rejects invalid module names", function(done) { - var promises = []; - var rejectedCount = 0; - - promises.push(installer.uninstallModule("this_wont_exist ").catch(() => {rejectedCount++})); - promises.push(installer.uninstallModule("this_wont_exist;no_it_really_wont").catch(() => {rejectedCount++})); - Promise.all(promises).then(function() { - rejectedCount.should.eql(2); - done(); - }).catch(done); - }); - - it("rejects with generic error", function(done) { - var nodeInfo = [{module:"foo",types:["a"]}]; - var removeModule = sinon.stub(registry,"removeModule").callsFake(function(md) { - return Promise.resolve(nodeInfo); - }); - var res = { - code: 1, - stdout:"", - stderr:"error" - } - var p = Promise.reject(res); - p.catch((err)=>{}); - execResponse = p; - - installer.uninstallModule("this_wont_exist").then(function() { - done(new Error("Unexpected success")); - }).catch(err => { - // Expected result - done() - }); - }); - it("succeeds when module is found", function(done) { - var nodeInfo = [{module:"foo",types:["a"]}]; - var removeModule = sinon.stub(typeRegistry,"removeModule").callsFake(function(md) { - return nodeInfo; - }); - var getModuleInfo = sinon.stub(registry,"getModuleInfo").callsFake(function(md) { - return {nodes:[]}; - }); - var res = { - code: 0, - stdout:"", - stderr:"" - } - var p = Promise.resolve(res); - p.catch((err)=>{}); - execResponse = p; - - sinon.stub(fs,"statSync").callsFake(function(fn) { return {}; }); - - installer.uninstallModule("this_wont_exist").then(function(info) { - info.should.eql(nodeInfo); - // commsMessages.should.have.length(1); - // commsMessages[0].topic.should.equal("node/removed"); - // commsMessages[0].msg.should.eql(nodeInfo); - done(); - }).catch(done); - }); - }); -}); diff --git a/test/unit/@node-red/registry/lib/library_spec.js b/test/unit/@node-red/registry/lib/library_spec.js deleted file mode 100644 index 2e0e7e99a..000000000 --- a/test/unit/@node-red/registry/lib/library_spec.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); - -var fs = require("fs"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var library = NR_TEST_UTILS.require("@node-red/registry/lib/library"); - -describe("library api", function() { - it('returns null list when no modules have been registered', function() { - library.init(); - should.not.exist(library.getExampleFlows()); - }); - it('returns null path when module is not known', function() { - library.init(); - should.not.exist(library.getExampleFlowPath('foo','bar')); - }); - - it('returns a valid example path', function(done) { - library.init(); - library.addExamplesDir("test-module",path.resolve(__dirname+'/resources/examples')).then(function() { - try { - var flows = library.getExampleFlows(); - flows.should.deepEqual({"test-module":{"f":["one"]}}); - - var examplePath = library.getExampleFlowPath('test-module','one'); - examplePath.should.eql(path.resolve(__dirname+'/resources/examples/one.json')) - - - library.removeExamplesDir('test-module'); - - try { - should.not.exist(library.getExampleFlows()); - should.not.exist(library.getExampleFlowPath('test-module','one')); - done(); - } catch(err) { - done(err); - } - }catch(err) { - done(err); - } - }); - - }) -}); diff --git a/test/unit/@node-red/registry/lib/loader_spec.js b/test/unit/@node-red/registry/lib/loader_spec.js deleted file mode 100644 index b9da833c8..000000000 --- a/test/unit/@node-red/registry/lib/loader_spec.js +++ /dev/null @@ -1,720 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var fs = require("fs-extra"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var loader = NR_TEST_UTILS.require("@node-red/registry/lib/loader"); - -var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem"); -var registry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); - -var nodes = NR_TEST_UTILS.require("@node-red/registry"); - -var resourcesDir = path.resolve(path.join(__dirname,"resources","local")); - -describe("red/nodes/registry/loader",function() { - var stubs = []; - before(function() { - sinon.stub(localfilesystem,"init"); - }); - after(function() { - localfilesystem.init.restore(); - }); - afterEach(function() { - while(stubs.length) { - stubs.pop().restore(); - } - }) - - describe("#load",function() { - it("load empty set without settings available", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ return {};})); - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return {};})); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return false;}}}); - loader.load(true).then(function() { - localfilesystem.getNodeFiles.called.should.be.true(); - localfilesystem.getNodeFiles.lastCall.args[0].should.be.true(); - registry.saveNodeList.called.should.be.false(); - done(); - }) - }); - it("load empty set with settings available triggers registery save", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ return {};})); - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return {};})); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load(true).then(function() { - registry.saveNodeList.called.should.be.true(); - done(); - }).catch(function(err) { - done(err); - }) - }); - - it("load core node files scanned by lfs - single node single file", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version": "1.2.3", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNode1","TestNode1.js"), - "module": "node-red", - "name": "TestNode1" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode1"); - module.nodes.TestNode1.should.have.property("id","node-red/TestNode1"); - module.nodes.TestNode1.should.have.property("module","node-red"); - module.nodes.TestNode1.should.have.property("name","TestNode1"); - module.nodes.TestNode1.should.have.property("file"); - module.nodes.TestNode1.should.have.property("template"); - module.nodes.TestNode1.should.have.property("enabled",true); - module.nodes.TestNode1.should.have.property("loaded",true); - module.nodes.TestNode1.should.have.property("types"); - module.nodes.TestNode1.types.should.have.a.length(1); - module.nodes.TestNode1.types[0].should.eql('test-node-1'); - module.nodes.TestNode1.should.have.property("config"); - module.nodes.TestNode1.config.should.not.eql(""); - module.nodes.TestNode1.should.have.property("help"); - module.nodes.TestNode1.help.should.have.property("en-US"); - module.nodes.TestNode1.should.have.property("namespace","node-red"); - - nodes.registerType.calledOnce.should.be.true(); - nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode1'); - nodes.registerType.lastCall.args[1].should.eql('test-node-1'); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("load core node files scanned by lfs - ignore html if disableEditor true", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version": "1.2.3", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNode1","TestNode1.js"), - "module": "node-red", - "name": "TestNode1" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{disableEditor: true, available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode1"); - module.nodes.TestNode1.should.have.property("id","node-red/TestNode1"); - module.nodes.TestNode1.should.have.property("module","node-red"); - module.nodes.TestNode1.should.have.property("name","TestNode1"); - module.nodes.TestNode1.should.have.property("file"); - module.nodes.TestNode1.should.have.property("template"); - module.nodes.TestNode1.should.have.property("enabled",true); - module.nodes.TestNode1.should.have.property("loaded",true); - // With disableEditor true, the types property is not populated by the - // html file - but instead is populated as nodes register themselves. - // But for this test, we have stubbed out registerType, so we won't get any types - // module.nodes.TestNode1.should.have.property("types"); - // module.nodes.TestNode1.types.should.have.a.length(1); - // module.nodes.TestNode1.types[0].should.eql('test-node-1'); - - // With disableEditor set, config should be blank - module.nodes.TestNode1.should.have.property("config"); - module.nodes.TestNode1.config.should.eql(""); - - // help should be an empty object - module.nodes.TestNode1.should.have.property("help"); - module.nodes.TestNode1.help.should.eql({}) - module.nodes.TestNode1.should.have.property("namespace","node-red"); - - nodes.registerType.calledOnce.should.be.true(); - nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode1'); - nodes.registerType.lastCall.args[1].should.eql('test-node-1'); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("load core node files scanned by lfs - multiple nodes single file", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version": "4.5.6", - "nodes": { - "MultipleNodes1": { - "file": path.join(resourcesDir,"MultipleNodes1","MultipleNodes1.js"), - "module": "node-red", - "name": "MultipleNodes1" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","4.5.6"); - module.should.have.property("nodes"); - module.nodes.should.have.property("MultipleNodes1"); - module.nodes.MultipleNodes1.should.have.property("id","node-red/MultipleNodes1"); - module.nodes.MultipleNodes1.should.have.property("module","node-red"); - module.nodes.MultipleNodes1.should.have.property("name","MultipleNodes1"); - module.nodes.MultipleNodes1.should.have.property("file"); - module.nodes.MultipleNodes1.should.have.property("template"); - module.nodes.MultipleNodes1.should.have.property("enabled",true); - module.nodes.MultipleNodes1.should.have.property("loaded",true); - module.nodes.MultipleNodes1.should.have.property("types"); - module.nodes.MultipleNodes1.types.should.have.a.length(2); - module.nodes.MultipleNodes1.types[0].should.eql('test-node-multiple-1a'); - module.nodes.MultipleNodes1.types[1].should.eql('test-node-multiple-1b'); - module.nodes.MultipleNodes1.should.have.property("config"); - module.nodes.MultipleNodes1.should.have.property("help"); - module.nodes.MultipleNodes1.should.have.property("namespace","node-red"); - - nodes.registerType.calledTwice.should.be.true(); - nodes.registerType.firstCall.args[0].should.eql('node-red/MultipleNodes1'); - nodes.registerType.firstCall.args[1].should.eql('test-node-multiple-1a'); - nodes.registerType.secondCall.args[0].should.eql('node-red/MultipleNodes1'); - nodes.registerType.secondCall.args[1].should.eql('test-node-multiple-1b'); - - - done(); - }).catch(function(err) { - done(err); - }); - }); - - - it("load core node files scanned by lfs - node with promise", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version":"2.4.6", - "nodes": { - "TestNode2": { - "file": path.join(resourcesDir,"TestNode2","TestNode2.js"), - "module": "node-red", - "name": "TestNode2" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","2.4.6"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode2"); - module.nodes.TestNode2.should.have.property("id","node-red/TestNode2"); - module.nodes.TestNode2.should.have.property("module","node-red"); - module.nodes.TestNode2.should.have.property("name","TestNode2"); - module.nodes.TestNode2.should.have.property("file"); - module.nodes.TestNode2.should.have.property("template"); - module.nodes.TestNode2.should.have.property("enabled",true); - module.nodes.TestNode2.should.have.property("loaded",true); - module.nodes.TestNode2.should.have.property("types"); - module.nodes.TestNode2.types.should.have.a.length(1); - module.nodes.TestNode2.types[0].should.eql('test-node-2'); - module.nodes.TestNode2.should.have.property("config"); - module.nodes.TestNode2.should.have.property("help"); - module.nodes.TestNode2.should.have.property("namespace","node-red"); - module.nodes.TestNode2.should.not.have.property('err'); - - nodes.registerType.calledOnce.should.be.true(); - nodes.registerType.lastCall.args[0].should.eql('node-red/TestNode2'); - nodes.registerType.lastCall.args[1].should.eql('test-node-2'); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - - it("load core node files scanned by lfs - node with rejecting promise", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version":"1.2.3", - "nodes": { - "TestNode3": { - "file": path.join(resourcesDir,"TestNode3","TestNode3.js"), - "module": "node-red", - "name": "TestNode3" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode3"); - module.nodes.TestNode3.should.have.property("id","node-red/TestNode3"); - module.nodes.TestNode3.should.have.property("module","node-red"); - module.nodes.TestNode3.should.have.property("name","TestNode3"); - module.nodes.TestNode3.should.have.property("file"); - module.nodes.TestNode3.should.have.property("template"); - module.nodes.TestNode3.should.have.property("enabled",true); - module.nodes.TestNode3.should.have.property("loaded",false); - module.nodes.TestNode3.should.have.property("types"); - module.nodes.TestNode3.types.should.have.a.length(1); - module.nodes.TestNode3.types[0].should.eql('test-node-3'); - module.nodes.TestNode3.should.have.property("config"); - module.nodes.TestNode3.should.have.property("help"); - module.nodes.TestNode3.should.have.property("namespace","node-red"); - module.nodes.TestNode3.should.have.property('err','fail'); - - nodes.registerType.called.should.be.false(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("load core node files scanned by lfs - missing file", function(done) { - stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - var result = {}; - result["node-red"] = { - "name": "node-red", - "version":"1.2.3", - "nodes": { - "DoesNotExist": { - "file": path.join(resourcesDir,"doesnotexist"), - "module": "node-red", - "name": "DoesNotExist" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.load().then(function(result) { - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","node-red"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("DoesNotExist"); - module.nodes.DoesNotExist.should.have.property("id","node-red/DoesNotExist"); - module.nodes.DoesNotExist.should.have.property("module","node-red"); - module.nodes.DoesNotExist.should.have.property("name","DoesNotExist"); - module.nodes.DoesNotExist.should.have.property("file"); - module.nodes.DoesNotExist.should.have.property("template"); - module.nodes.DoesNotExist.should.have.property("enabled",true); - module.nodes.DoesNotExist.should.have.property("loaded",false); - module.nodes.DoesNotExist.should.have.property("types"); - module.nodes.DoesNotExist.types.should.have.a.length(0); - module.nodes.DoesNotExist.should.have.property("config",""); - module.nodes.DoesNotExist.should.have.property("help",{}); - module.nodes.DoesNotExist.should.have.property("namespace","node-red"); - module.nodes.DoesNotExist.should.have.property('err'); - - nodes.registerType.called.should.be.false(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - // it("load core node files scanned by lfs - missing html file", function(done) { - // // This is now an okay situation - // stubs.push(sinon.stub(localfilesystem,"getNodeFiles").callsFake(function(){ - // var result = {}; - // result["node-red"] = { - // "name": "node-red", - // "version": "1.2.3", - // "nodes": { - // "DuffNode": { - // "file": path.join(resourcesDir,"DuffNode","DuffNode.js"), - // "module": "node-red", - // "name": "DuffNode" - // } - // } - // }; - // return result; - // })); - // - // stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return })); - // stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - // // This module isn't already loaded - // stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - // - // stubs.push(sinon.stub(nodes,"registerType")); - // loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - // loader.load().then(function(result) { - // - // registry.addModule.called.should.be.true(); - // var module = registry.addModule.lastCall.args[0]; - // module.should.have.property("name","node-red"); - // module.should.have.property("version","1.2.3"); - // module.should.have.property("nodes"); - // module.nodes.should.have.property("DuffNode"); - // module.nodes.DuffNode.should.have.property("id","node-red/DuffNode"); - // module.nodes.DuffNode.should.have.property("module","node-red"); - // module.nodes.DuffNode.should.have.property("name","DuffNode"); - // module.nodes.DuffNode.should.have.property("file"); - // module.nodes.DuffNode.should.have.property("template"); - // module.nodes.DuffNode.should.have.property("enabled",true); - // module.nodes.DuffNode.should.have.property("loaded",false); - // module.nodes.DuffNode.should.have.property("types"); - // module.nodes.DuffNode.types.should.have.a.length(0); - // module.nodes.DuffNode.should.have.property("config",""); - // module.nodes.DuffNode.should.have.property("help",{}); - // module.nodes.DuffNode.should.have.property("namespace","node-red"); - // module.nodes.DuffNode.should.have.property('err'); - // module.nodes.DuffNode.err.should.endWith("DuffNode.html does not exist"); - // - // nodes.registerType.called.should.be.false(); - // - // done(); - // }).catch(function(err) { - // done(err); - // }); - // }); - }); - - describe("#addModule",function() { - it("throws error if settings unavailable", function() { - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return false;}}}); - /*jshint immed: false */ - (function(){ - loader.addModule("test-module"); - }).should.throw("Settings unavailable"); - }); - - it("returns rejected error if module already loaded", function(done) { - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){return{}})); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - - loader.addModule("test-module").catch(function(err) { - err.code.should.eql("module_already_loaded"); - done(); - }); - }); - it("returns rejected error if module not found", function(done) { - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){return null})); - stubs.push(sinon.stub(localfilesystem,"getModuleFiles").callsFake(function() { - throw new Error("failure"); - })); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.addModule("test-module").catch(function(err) { - err.message.should.eql("failure"); - done(); - }); - - }); - - it("loads module by name", function(done) { - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(localfilesystem,"getModuleFiles").callsFake(function(){ - var result = {}; - result["TestNodeModule"] = { - "name": "TestNodeModule", - "version": "1.2.3", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNodeModule","node_modules","TestNodeModule","TestNodeModule.js"), - "module": "TestNodeModule", - "name": "TestNode1", - "version": "1.2.3" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return "a node list" })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({nodes:nodes,log:{info:function(){},_:function(){}},settings:{available:function(){return true;}}}); - loader.addModule("TestNodeModule").then(function(result) { - result.should.eql("TestNodeModule"); - - registry.addModule.called.should.be.true(); - var module = registry.addModule.lastCall.args[0]; - module.should.have.property("name","TestNodeModule"); - module.should.have.property("version","1.2.3"); - module.should.have.property("nodes"); - module.nodes.should.have.property("TestNode1"); - module.nodes.TestNode1.should.have.property("id","TestNodeModule/TestNode1"); - module.nodes.TestNode1.should.have.property("module","TestNodeModule"); - module.nodes.TestNode1.should.have.property("name","TestNode1"); - module.nodes.TestNode1.should.have.property("file"); - module.nodes.TestNode1.should.have.property("template"); - module.nodes.TestNode1.should.have.property("enabled",true); - module.nodes.TestNode1.should.have.property("loaded",true); - module.nodes.TestNode1.should.have.property("types"); - module.nodes.TestNode1.types.should.have.a.length(1); - module.nodes.TestNode1.types[0].should.eql('test-node-mod-1'); - module.nodes.TestNode1.should.have.property("config"); - module.nodes.TestNode1.should.have.property("help"); - module.nodes.TestNode1.should.have.property("namespace","TestNodeModule"); - module.nodes.TestNode1.should.not.have.property('err'); - - nodes.registerType.calledOnce.should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("skips module that fails version check", function(done) { - // This module isn't already loaded - stubs.push(sinon.stub(registry,"getNodeInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(registry,"getModuleInfo").callsFake(function(){ return null; })); - stubs.push(sinon.stub(localfilesystem,"getModuleFiles").callsFake(function(){ - var result = {}; - result["TestNodeModule"] = { - "name": "TestNodeModule", - "version": "1.2.3", - "redVersion":"999.0.0", - "nodes": { - "TestNode1": { - "file": path.join(resourcesDir,"TestNodeModule","node_modules","TestNodeModule","TestNodeModule.js"), - "module": "TestNodeModule", - "name": "TestNode1", - "version": "1.2.3" - } - } - }; - return result; - })); - - stubs.push(sinon.stub(registry,"saveNodeList").callsFake(function(){ return "a node list" })); - stubs.push(sinon.stub(registry,"addModule").callsFake(function(){ return })); - stubs.push(sinon.stub(nodes,"registerType")); - loader.init({log:{"_":function(){},warn:function(){}},nodes:nodes,version: function() { return "0.12.0"}, settings:{available:function(){return true;}}}); - loader.addModule("TestNodeModule").then(function(result) { - result.should.eql("TestNodeModule"); - registry.addModule.called.should.be.false(); - nodes.registerType.called.should.be.false(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it.skip('registers a message catalog'); - - - }); - describe("#loadNodeSet",function() { - it("no-ops the load if node is not enabled", function(done) { - stubs.push(sinon.stub(nodes,"registerType")); - loader.loadNodeSet({ - "file": path.join(resourcesDir,"TestNode1","TestNode1.js"), - "module": "node-red", - "name": "TestNode1", - "enabled": false - }).then(function(node) { - node.enabled.should.be.false(); - nodes.registerType.called.should.be.false(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it("handles node that errors on require", function(done) { - stubs.push(sinon.stub(nodes,"registerType")); - loader.loadNodeSet({ - "file": path.join(resourcesDir,"TestNode4","TestNode4.js"), - "module": "node-red", - "name": "TestNode4", - "enabled": true - }).then(function(node) { - node.enabled.should.be.true(); - nodes.registerType.called.should.be.false(); - node.should.have.property('err'); - node.err.toString().should.eql("Error: fail to require (line:1)"); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - describe("#getNodeHelp",function() { - it("returns preloaded help", function() { - loader.getNodeHelp({ - help:{ - en:"foo" - } - },"en").should.eql("foo"); - }); - it("loads help, caching result", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - return 'bar'; - })) - var node = { - template: "/tmp/node/directory/file.html", - help:{ - en:"foo" - } - }; - loader.getNodeHelp(node,"fr").should.eql("bar"); - node.help['fr'].should.eql("bar"); - fs.readFileSync.calledOnce.should.be.true(); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/fr/file.html")); - loader.getNodeHelp(node,"fr").should.eql("bar"); - fs.readFileSync.calledOnce.should.be.true(); - }); - it("loads help, defaulting to en-US content", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - throw new Error("not found"); - })) - var node = { - template: "/tmp/node/directory/file.html", - help:{} - }; - node.help['en-US'] = 'foo'; - - loader.getNodeHelp(node,"fr").should.eql("foo"); - node.help['fr'].should.eql("foo"); - fs.readFileSync.calledOnce.should.be.true(); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/fr/file.html")); - loader.getNodeHelp(node,"fr").should.eql("foo"); - fs.readFileSync.calledOnce.should.be.true(); - }); - it("loads help, defaulting to en-US content for extra nodes", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - if (path.indexOf("en-US") >= 0) { - return 'bar'; - } - throw new Error("not found"); - })); - var node = { - template: "/tmp/node/directory/file.html", - help:{} - }; - delete node.help['en-US']; - - loader.getNodeHelp(node,"fr").should.eql("bar"); - node.help['fr'].should.eql("bar"); - fs.readFileSync.calledTwice.should.be.true(); - fs.readFileSync.firstCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/fr/file.html")); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/en-US/file.html")); - loader.getNodeHelp(node,"fr").should.eql("bar"); - fs.readFileSync.calledTwice.should.be.true(); - }); - it("fails to load en-US help content", function() { - stubs.push(sinon.stub(fs,"readFileSync").callsFake(function(path) { - throw new Error("not found"); - })); - var node = { - template: "/tmp/node/directory/file.html", - help:{} - }; - delete node.help['en-US']; - - should.not.exist(loader.getNodeHelp(node,"en-US")); - should.not.exist(node.help['en-US']); - fs.readFileSync.calledTwice.should.be.true(); - fs.readFileSync.firstCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/en-US/file.html")); - fs.readFileSync.lastCall.args[0].should.eql(path.normalize("/tmp/node/directory/locales/en/file.html")); - should.not.exist(loader.getNodeHelp(node,"en-US")); - fs.readFileSync.callCount.should.eql(4); - }); - - }); -}); diff --git a/test/unit/@node-red/registry/lib/localfilesystem_spec.js b/test/unit/@node-red/registry/lib/localfilesystem_spec.js deleted file mode 100644 index e84761657..000000000 --- a/test/unit/@node-red/registry/lib/localfilesystem_spec.js +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem"); - -var resourcesDir = path.resolve(path.join(__dirname,"resources","local")); -var userDir = path.resolve(path.join(__dirname,"resources","userDir")); -var moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule")); - -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("red/nodes/registry/localfilesystem",function() { - beforeEach(function() { - stubs.push(sinon.stub(i18n,"registerMessageCatalog").callsFake(function() { return Promise.resolve(); })); - }) - - var stubs = []; - afterEach(function() { - while(stubs.length) { - stubs.pop().restore(); - } - }) - function checkNodes(nodes,shouldHaveNodes,shouldNotHaveNodes,module) { - for (var i=0;i modules[moduleId]); - sinon.stub(registry,"getModuleList").callsFake(() => modules) - }); - afterEach(function() { - events.removeListener("registry:plugin-added",handleEvent); - registry.getModule.restore(); - registry.getModuleList.restore(); - }) - - describe("registerPlugin", function() { - it("registers a plugin", function() { - let pluginDef = {} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - receivedEvents.length.should.eql(1); - receivedEvents[0].should.eql("a-plugin"); - should.exist(modules['test-module'].plugins['test-set'].plugins[0]) - modules['test-module'].plugins['test-set'].plugins[0].should.equal(pluginDef) - }) - it("calls a plugins onadd function", function() { - let pluginDef = { onadd: sinon.stub() } - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - pluginDef.onadd.called.should.be.true(); - }) - }) - - describe("getPlugin", function() { - it("returns a registered plugin", function() { - let pluginDef = {} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - pluginDef.should.equal(plugins.getPlugin("a-plugin")); - }) - }) - describe("getPluginsByType", function() { - it("returns a plugins of a given type", function() { - let pluginDef = {type: "foo"} - let pluginDef2 = {type: "bar"} - let pluginDef3 = {type: "foo"} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2); - plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3); - - let fooPlugins = plugins.getPluginsByType("foo"); - let barPlugins = plugins.getPluginsByType("bar"); - let noPlugins = plugins.getPluginsByType("none"); - - noPlugins.should.be.of.length(0); - - fooPlugins.should.be.of.length(2); - fooPlugins.should.containEql(pluginDef); - fooPlugins.should.containEql(pluginDef3); - - barPlugins.should.be.of.length(1); - barPlugins.should.containEql(pluginDef2); - - }) - }) - - describe("getPluginConfigs", function() { - it("gets all plugin configs", function() { - let configs = plugins.getPluginConfigs("en-US"); - configs.should.eql(` - -test-module-config`) - }) - }) - - - describe("getPluginList", function() { - it("returns a plugins of a given type", function() { - let pluginDef = {type: "foo"} - let pluginDef2 = {type: "bar"} - let pluginDef3 = {type: "foo"} - plugins.registerPlugin("test-module/test-set","a-plugin",pluginDef); - plugins.registerPlugin("test-module/test-set","a-plugin2",pluginDef2); - plugins.registerPlugin("test-module/test-set","a-plugin3",pluginDef3); - - let pluginList = plugins.getPluginList(); - JSON.stringify(pluginList).should.eql(JSON.stringify( - [ - { - "id": "test-module/test-set", - "enabled": true, - "local": false, - "user": false, - "plugins": [ - { - "type": "foo", - "id": "a-plugin", - "module": "test-module" - }, - { - "type": "bar", - "id": "a-plugin2", - "module": "test-module" - }, - { - "type": "foo", - "id": "a-plugin3", - "module": "test-module" - } - ] - }, - { - "id": "test-module/test-disabled-set", - "enabled": false, - "local": false, - "user": false, - "plugins": [] - } - ] - )) - }) - }) - describe("exportPluginSettings", function() { - it("exports plugin settings - default false", function() { - plugins.init({ "a-plugin": { a: 123, b:234, c: 345} }); - plugins.registerPlugin("test-module/test-set","a-plugin",{ - settings: { - a: { exportable: true }, - b: {exportable: false }, - d: { exportable: true, value: 456} - - } - }); - var exportedSet = {}; - plugins.exportPluginSettings(exportedSet); - exportedSet.should.have.property("a-plugin"); - // a is exportable - exportedSet["a-plugin"].should.have.property("a",123); - // b is explicitly not exportable - exportedSet["a-plugin"].should.not.have.property("b"); - // c isn't listed and default false - exportedSet["a-plugin"].should.not.have.property("c"); - // d has a default value - exportedSet["a-plugin"].should.have.property("d",456); - }) - it("exports plugin settings - default true", function() { - plugins.init({ "a-plugin": { a: 123, b:234, c: 345} }); - plugins.registerPlugin("test-module/test-set","a-plugin",{ - settings: { - '*': { exportable: true }, - a: { exportable: true }, - b: {exportable: false }, - d: { exportable: true, value: 456} - - } - }); - var exportedSet = {}; - plugins.exportPluginSettings(exportedSet); - exportedSet.should.have.property("a-plugin"); - // a is exportable - exportedSet["a-plugin"].should.have.property("a",123); - // b is explicitly not exportable - exportedSet["a-plugin"].should.not.have.property("b"); - // c isn't listed, but default true - exportedSet["a-plugin"].should.have.property("c"); - // d has a default value - exportedSet["a-plugin"].should.have.property("d",456); - }) - }); -}); diff --git a/test/unit/@node-red/registry/lib/registry_spec.js b/test/unit/@node-red/registry/lib/registry_spec.js deleted file mode 100644 index 5501264a7..000000000 --- a/test/unit/@node-red/registry/lib/registry_spec.js +++ /dev/null @@ -1,614 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry/lib/registry"); -const { events } = NR_TEST_UTILS.require("@node-red/util"); - -describe("red/nodes/registry/registry",function() { - - afterEach(function() { - typeRegistry.clear(); - }); - - function stubSettings(s,available,initialConfig) { - s.available = function() {return available;}; - s.set = sinon.spy(function(s,v) { return Promise.resolve();}); - s.get = function(s) { return initialConfig;}; - return s; - } - - var settings = stubSettings({},false,null); - var settingsWithStorageAndInitialConfig = stubSettings({},true,{"node-red":{module:"testModule",name:"testName",version:"testVersion",nodes:{"node":{id:"node-red/testName",name:"test",types:["a","b"],enabled:true}}}}); - - var testNodeSet1 = { - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"] - }; - - var testNodeSet2 = { - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-c","test-d"] - }; - var testNodeSet2WithError = { - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - err: "I have an error", - config: "configC", - types: [ "test-c","test-d"] - }; - var testNodeSet3 = { - id: "test-module-2/test-name-3", - module: "test-module-2", - name: "test-name-3", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-a","test-e"] - }; - - - - describe('#init/load', function() { - it('loads initial config', function(done) { - typeRegistry.init(settingsWithStorageAndInitialConfig,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.load(); - typeRegistry.getNodeList().should.have.lengthOf(1); - done(); - }); - - it('migrates legacy format', function(done) { - var legacySettings = { - available: function() { return true; }, - set: sinon.stub().returns(Promise.resolve()), - get: function() { return { - "123": { - "name": "72-sentiment.js", - "types": [ - "sentiment" - ], - "enabled": true - }, - "456": { - "name": "20-inject.js", - "types": [ - "inject" - ], - "enabled": true - }, - "789": { - "name": "testModule:a-module.js", - "types": [ - "example" - ], - "enabled":true, - "module":"testModule" - } - }} - }; - var expected = JSON.parse('{"node-red":{"name":"node-red","nodes":{"sentiment":{"name":"sentiment","types":["sentiment"],"enabled":true,"module":"node-red"},"inject":{"name":"inject","types":["inject"],"enabled":true,"module":"node-red"}}},"testModule":{"name":"testModule","nodes":{"a-module.js":{"name":"a-module.js","types":["example"],"enabled":true,"module":"testModule"}}}}'); - typeRegistry.init(legacySettings,null); - typeRegistry.load(); - legacySettings.set.calledOnce.should.be.true(); - legacySettings.set.args[0][1].should.eql(expected); - done(); - }); - }); - - - describe.skip('#addNodeSet', function() { - it('adds a node set for an unknown module', function() { - - typeRegistry.init(settings,null); - - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - - typeRegistry.getNodeList().should.have.lengthOf(1); - var moduleList = typeRegistry.getModuleList(); - moduleList.should.have.a.property("test-module"); - moduleList["test-module"].should.have.a.property("name","test-module"); - moduleList["test-module"].should.have.a.property("version","0.0.1"); - moduleList["test-module"].should.have.a.property("nodes"); - moduleList["test-module"].nodes.should.have.a.property("test-name"); - - moduleList["test-module"].nodes["test-name"].should.eql({ - config: 'configA', - id: 'test-module/test-name', - module: 'test-module', - name: 'test-name', - enabled: true, - loaded: false, - types: [ 'test-a', 'test-b' ] - }); - - }); - - it('adds a node set to an existing module', function() { - - typeRegistry.init(settings,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - - typeRegistry.getNodeList().should.have.lengthOf(1); - var moduleList = typeRegistry.getModuleList(); - Object.keys(moduleList).should.have.a.lengthOf(1); - moduleList.should.have.a.property("test-module"); - moduleList["test-module"].should.have.a.property("name","test-module"); - moduleList["test-module"].should.have.a.property("version","0.0.1"); - moduleList["test-module"].should.have.a.property("nodes"); - - Object.keys(moduleList["test-module"].nodes).should.have.a.lengthOf(1); - moduleList["test-module"].nodes.should.have.a.property("test-name"); - - - typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2); - - typeRegistry.getNodeList().should.have.lengthOf(2); - moduleList = typeRegistry.getModuleList(); - Object.keys(moduleList).should.have.a.lengthOf(1); - Object.keys(moduleList["test-module"].nodes).should.have.a.lengthOf(2); - moduleList["test-module"].nodes.should.have.a.property("test-name"); - moduleList["test-module"].nodes.should.have.a.property("test-name-2"); - }); - - it('doesnt add node set types if node set has an error', function() { - typeRegistry.init(settings,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - - typeRegistry.getTypeId("test-a").should.eql("test-module/test-name"); - - should.not.exist(typeRegistry.getTypeId("test-c")); - - typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2WithError, "0.0.1"); - - should.not.exist(typeRegistry.getTypeId("test-c")); - }); - - it('doesnt add node set if type already exists', function() { - typeRegistry.init(settings,null); - typeRegistry.getNodeList().should.have.lengthOf(0); - typeRegistry.getModuleList().should.eql({}); - - should.not.exist(typeRegistry.getTypeId("test-e")); - - typeRegistry.addNodeSet("test-module/test-name",testNodeSet1, "0.0.1"); - typeRegistry.getNodeList().should.have.lengthOf(1); - should.exist(typeRegistry.getTypeId("test-a")); - typeRegistry.addNodeSet(testNodeSet3.id,testNodeSet3, "0.0.1"); - typeRegistry.getNodeList().should.have.lengthOf(2); - - // testNodeSet3 registers a duplicate test-a and unique test-e - // as test-a is a duplicate, test-e should not get registered - should.not.exist(typeRegistry.getTypeId("test-e")); - - var testNodeSet3Result = typeRegistry.getNodeList()[1]; - should.exist(testNodeSet3Result.err); - testNodeSet3Result.err.code.should.equal("type_already_registered"); - testNodeSet3Result.err.details.type.should.equal("test-a"); - testNodeSet3Result.err.details.moduleA.should.equal("test-module"); - testNodeSet3Result.err.details.moduleB.should.equal("test-module-2"); - - // - // typeRegistry.addNodeSet("test-module/test-name-2",testNodeSet2WithError, "0.0.1"); - // - // should.not.exist(typeRegistry.getTypeId("test-c")); - }); - - - }); - - describe("#enableNodeSet", function() { - it('throws error if settings unavailable', function() { - typeRegistry.init(settings,null); - /*jshint immed: false */ - (function(){ - typeRegistry.enableNodeSet("test-module/test-name"); - }).should.throw("Settings unavailable"); - }); - - it('throws error if module unknown', function() { - typeRegistry.init(settingsWithStorageAndInitialConfig,null); - /*jshint immed: false */ - (function(){ - typeRegistry.enableNodeSet("test-module/unknown"); - }).should.throw("Unrecognised id: test-module/unknown"); - }); - it.skip('enables the node',function(){}) - - }); - describe("#disableNodeSet", function() { - it('throws error if settings unavailable', function() { - typeRegistry.init(settings,null); - /*jshint immed: false */ - (function(){ - typeRegistry.disableNodeSet("test-module/test-name"); - }).should.throw("Settings unavailable"); - }); - - it('throws error if module unknown', function() { - typeRegistry.init(settingsWithStorageAndInitialConfig,null); - /*jshint immed: false */ - (function(){ - typeRegistry.disableNodeSet("test-module/unknown"); - }).should.throw("Unrecognised id: test-module/unknown"); - }); - it.skip('disables the node',function(){}) - }); - - describe('#getNodeConfig', function() { - it('returns nothing for an unregistered type config', function(done) { - typeRegistry.init(settings,null); - var config = typeRegistry.getNodeConfig("imaginary-shark"); - (config === null).should.be.true(); - done(); - }); - }); - - describe('#saveNodeList',function() { - it('rejects when settings unavailable',function(done) { - typeRegistry.init(stubSettings({},false,{}),null); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: {"test-name":{module:"test-module",name:"test-name",types:[]}}}); - typeRegistry.saveNodeList().catch(function(err) { - done(); - }); - }); - it('saves the list',function(done) { - var s = stubSettings({},true,{}); - typeRegistry.init(s,null); - - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":testNodeSet1, - "test-name-2":testNodeSet2WithError - }}); - - typeRegistry.saveNodeList().then(function() { - s.set.called.should.be.true(); - s.set.lastCall.args[0].should.eql('nodes'); - var nodes = s.set.lastCall.args[1]; - nodes.should.have.property('test-module'); - for (var n in nodes['test-module'].nodes) { - if (nodes['test-module'].nodes.hasOwnProperty(n)) { - var nn = nodes['test-module'].nodes[n]; - nn.should.not.have.property('err'); - nn.should.not.have.property('id'); - } - } - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe('#removeModule',function() { - it('throws error for unknown module', function() { - var s = stubSettings({},true,{}); - typeRegistry.init(s,null); - /*jshint immed: false */ - (function(){ - typeRegistry.removeModule("test-module/unknown"); - }).should.throw("Unrecognised module: test-module/unknown"); - }); - it('throws error for unavaiable settings', function() { - var s = stubSettings({},false,{}); - typeRegistry.init(s,null); - /*jshint immed: false */ - (function(){ - typeRegistry.removeModule("test-module/unknown"); - }).should.throw("Settings unavailable"); - }); - it('removes a known module', function() { - var s = stubSettings({},true,{}); - typeRegistry.init(s,null); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":testNodeSet1 - }}); - var moduleList = typeRegistry.getModuleList(); - moduleList.should.have.a.property("test-module"); - typeRegistry.getNodeList().should.have.lengthOf(1); - - var info = typeRegistry.removeModule('test-module'); - moduleList = typeRegistry.getModuleList(); - moduleList.should.not.have.a.property("test-module"); - typeRegistry.getNodeList().should.have.lengthOf(0); - }); - }); - - describe('#get[All]NodeConfigs', function() { - it('returns node config', function() { - typeRegistry.init(settings,{ - getNodeHelp: function(config) { return "HE"+config.name+"LP" } - }); - - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"] - }, - "test-name-2":{ - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-c","test-d"] - } - }}); - typeRegistry.getNodeConfig("test-module/test-name").should.eql('\nconfigAHEtest-nameLP'); - typeRegistry.getNodeConfig("test-module/test-name-2").should.eql('\nconfigBHEtest-name-2LP'); - typeRegistry.getAllNodeConfigs().should.eql('\n\nconfigAHEtest-nameLP\n\nconfigBHEtest-name-2LP'); - }); - }); - describe('#getModuleInfo', function() { - it('returns module info', function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - }}); - var moduleInfo = typeRegistry.getModuleInfo("test-module"); - moduleInfo.should.have.a.property('name','test-module'); - moduleInfo.should.have.a.property('version','0.0.1'); - moduleInfo.should.have.a.property('nodes'); - moduleInfo.nodes.should.have.a.lengthOf(1); - moduleInfo.nodes[0].should.have.a.property('id','test-module/test-name'); - moduleInfo.nodes[0].should.not.have.a.property('file'); - }); - }); - describe('#getNodeInfo', function() { - it('returns node info', function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - }}); - var nodeSetInfo = typeRegistry.getNodeInfo("test-module/test-name"); - nodeSetInfo.should.have.a.property('id',"test-module/test-name"); - nodeSetInfo.should.not.have.a.property('config'); - nodeSetInfo.should.not.have.a.property('file'); - }); - }); - describe('#getFullNodeInfo', function() { - it('returns node info', function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - - } - }}); - var nodeSetInfo = typeRegistry.getFullNodeInfo("test-module/test-name"); - nodeSetInfo.should.have.a.property('id',"test-module/test-name"); - nodeSetInfo.should.have.a.property('config'); - nodeSetInfo.should.have.a.property('file'); - }); - }); - describe('#cleanModuleList', function() { - it.skip("cleans the module list"); - }); - describe('#getNodeList', function() { - it("returns a filtered list", function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - }, - "test-name-2":{ - id: "test-module/test-name-2", - module: "test-module", - name: "test-name-2", - enabled: true, - loaded: false, - config: "configB", - types: [ "test-c","test-d"], - file: "def" - } - }}); - var filterCallCount = 0; - var filteredList = typeRegistry.getNodeList(function(n) { filterCallCount++; return n.name === 'test-name-2';}); - filterCallCount.should.eql(2); - filteredList.should.have.a.lengthOf(1); - filteredList[0].should.have.a.property('id',"test-module/test-name-2"); - }); - }); - - describe('#registerNodeConstructor', function() { - var TestNodeConstructor; - beforeEach(function() { - TestNodeConstructor = function TestNodeConstructor() {}; - sinon.stub(events,'emit'); - }); - afterEach(function() { - events.emit.restore(); - }); - it('registers a node constructor', function() { - typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor); - events.emit.calledOnce.should.be.true(); - events.emit.lastCall.args[0].should.eql('type-registered'); - events.emit.lastCall.args[1].should.eql('node-type'); - }) - it('throws error on duplicate node registration', function() { - typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor); - events.emit.calledOnce.should.be.true(); - events.emit.lastCall.args[0].should.eql('type-registered'); - events.emit.lastCall.args[1].should.eql('node-type'); - /*jshint immed: false */ - (function(){ - typeRegistry.registerNodeConstructor('node-set','node-type',TestNodeConstructor); - }).should.throw("node-type already registered"); - events.emit.calledOnce.should.be.true(); - }); - }); - - describe('#getNodeIconPath', function() { - it('returns the null when getting an unknown icon', function() { - var iconPath = typeRegistry.getNodeIconPath('random-module','youwonthaveme.png'); - should.not.exist(iconPath); - }); - - it('returns a registered icon' , function() { - var testIcon = path.resolve(__dirname+'/resources/userDir/lib/icons/'); - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - },icons: [{path:testIcon,icons:['test_icon.png']}]}); - var iconPath = typeRegistry.getNodeIconPath('test-module','test_icon.png'); - iconPath.should.eql(path.resolve(testIcon+"/test_icon.png")); - }); - - it('returns null when getting an unknown module', function() { - var debugIcon = path.resolve(__dirname+'/../../../public/icons/debug.png'); - var iconPath = typeRegistry.getNodeIconPath('unknown-module', 'debug.png'); - should.not.exist(iconPath); - }); - }); - - describe('#getNodeIcons', function() { - it('returns empty icon list when no modules are registered', function() { - var iconList = typeRegistry.getNodeIcons(); - iconList.should.eql({}); - }); - - it('returns an icon list of registered node module', function() { - var testIcon = path.resolve(__dirname+'/resources/userDir/lib/icons/'); - typeRegistry.init(settings,{}); - typeRegistry.addModule({name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - },icons: [{path:testIcon,icons:['test_icon.png']}]}); - var iconList = typeRegistry.getNodeIcons(); - iconList.should.eql({"test-module":["test_icon.png"]}); - }); - }); - - describe('#getModuleResource', function() { - beforeEach(function() { - typeRegistry.init(settings,{}); - typeRegistry.addModule({ - name: "test-module",version:"0.0.1",nodes: { - "test-name":{ - id: "test-module/test-name", - module: "test-module", - name: "test-name", - enabled: true, - loaded: false, - config: "configA", - types: [ "test-a","test-b"], - file: "abc" - } - }, - resources: { - path: path.join(__dirname, "resources","examples") - } - }); - }); - it('Returns valid resource path', function() { - const result = typeRegistry.getModuleResource("test-module","one.json"); - should.exist(result); - result.should.eql(path.join(__dirname, "resources","examples","one.json")) - }); - it('Returns null for path that tries to break out', function() { - // Note - this path exists, but we don't allow .. in the resolved path to - // avoid breaking out of the resources dir - const result = typeRegistry.getModuleResource("test-module","../../index_spec.js"); - should.not.exist(result); - }); - it('Returns null for path that does not exist', function() { - const result = typeRegistry.getModuleResource("test-module","two.json"); - should.not.exist(result); - }); - }); -}); diff --git a/test/unit/@node-red/registry/lib/resources/examples/one.json b/test/unit/@node-red/registry/lib/resources/examples/one.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js b/test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js deleted file mode 100644 index 41e2a0059..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/DuffNode/DuffNode.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function DuffNode(n) {} - RED.nodes.registerType("duff-node",DuffNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html b/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html deleted file mode 100644 index b637ede21..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js b/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js deleted file mode 100644 index e81214169..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/DuplicateTestNode/TestNode1.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function DuplicateTestNode(n) {} - RED.nodes.registerType("test-node-1",DuplicateTestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html b/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html deleted file mode 100644 index 5359644e5..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js b/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js deleted file mode 100644 index 55747c0b3..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/MultipleNodes1/MultipleNodes1.js +++ /dev/null @@ -1,7 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode1(n) {} - RED.nodes.registerType("test-node-multiple-1a",TestNode1); - function TestNode2(n) {} - RED.nodes.registerType("test-node-multiple-1b",TestNode2); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html deleted file mode 100644 index abc823e8f..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js deleted file mode 100644 index cd3148a58..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/NestedNode.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("nested-node-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png deleted file mode 100644 index e38f3914600901b736f5fa18786ee11be6d41c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6icy_X9x!n)NrJ90QsB+9+AZi z3~X;em{G3O!W1YdS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|Bb%p- zV~9uR+N-+$hZ97Oeyp#Y$RU!&XX#m>@|$agvYL9Jz$O6}72y^gZVi>ZMeaH^t*Q|! zLhVk<0xdko8zYVo#8M-LBBd8b>1kw|i?FO)bWqsy_rwQg27N4y zeX6pqO$_Cex^^A7_NsS@+=bOASA^wN0&GKN(hlAguRU&q- jTBY?`*L>xN+|S&+7B*edvmQhOgN?z{)z4*}Q$iB}+9!`i diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html deleted file mode 100644 index ac9235d26..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js deleted file mode 100644 index 8af249b14..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/lib/ShouldNotLoad.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("should-not-load-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html deleted file mode 100644 index eb7c8a3f9..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js deleted file mode 100644 index 623e299b2..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/node_modules/ShouldNotLoad.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("should-not-load-2",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html deleted file mode 100644 index 4212fd589..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js b/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js deleted file mode 100644 index 5856adada..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/NestedDirectoryNode/NestedNode/test/ShouldNotLoad.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("should-not-load-3",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html b/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html deleted file mode 100644 index 97dbf1710..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js b/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js deleted file mode 100644 index bfa3b65bd..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode1/TestNode1.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("test-node-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html b/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html deleted file mode 100644 index 66b65909e..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js b/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js deleted file mode 100644 index 1bf2fa6c9..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode2/TestNode2.js +++ /dev/null @@ -1,9 +0,0 @@ -// A test node that exports a function which returns a resolving promise - -module.exports = function(RED) { - return new Promise(function(resolve,reject) { - function TestNode(n) {} - RED.nodes.registerType("test-node-2",TestNode); - resolve(); - }); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html b/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html deleted file mode 100644 index 9a0f6f7ea..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js b/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js deleted file mode 100644 index b9ee6a624..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode3/TestNode3.js +++ /dev/null @@ -1,7 +0,0 @@ -// A test node that exports a function which returns a rejecting promise - -module.exports = function(RED) { - return new Promise(function(resolve,reject) { - reject("fail"); - }); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html b/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html deleted file mode 100644 index 9a0f6f7ea..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js b/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js deleted file mode 100644 index c31255852..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNode4/TestNode4.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("fail to require"); diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt deleted file mode 100644 index 0ce8dcaed..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/EmptyModule/file.txt +++ /dev/null @@ -1 +0,0 @@ -This file exists just to ensure the parent directory is in the repository. diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html deleted file mode 100644 index 17483f7ca..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js deleted file mode 100644 index c79c2ceb6..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("test-node-mod-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html deleted file mode 100644 index a1f1b6c79..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js deleted file mode 100644 index d359fb3f2..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/TestNodeModule2.js +++ /dev/null @@ -1,4 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - throw new Error("fail to load"); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png deleted file mode 100644 index e38f3914600901b736f5fa18786ee11be6d41c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6icy_X9x!n)NrJ90QsB+9+AZi z3~X;em{G3O!W1YdS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|Bb%p- zV~9uR+N-+$hZ97Oeyp#Y$RU!&XX#m>@|$agvYL9Jz$O6}72y^gZVi>ZMeaH^t*Q|! zLhVk<0xdko8zYVo#8M-LBBd8b>1kw|i?FO)bWqsy_rwQg27N4y zeX6pqO$_Cex^^A7_NsS@+=bOASA^wN0&GKN(hlAguRU&q- jTBY?`*L>xN+|S&+7B*edvmQhOgN?z{)z4*}Q$iB}+9!`i diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt deleted file mode 100644 index 59a29af14..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/file.txt +++ /dev/null @@ -1,3 +0,0 @@ -This file exists just to ensure the 'icons' directory is in the repository. -TODO: a future test needs to ensure the right icon files are loaded - this - directory can be used for that diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json deleted file mode 100644 index 8eba5b04b..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/TestNodeModule/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name" : "TestNodeModule", - "version" : "0.0.1", - "description" : "A test node module", - "node-red" : { - "nodes": { - "TestNodeMod1": "TestNodeModule.js", - "TestNodeMod2": "TestNodeModule2.js" - } - } -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html deleted file mode 100644 index 17483f7ca..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js deleted file mode 100644 index c79c2ceb6..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule.js +++ /dev/null @@ -1,5 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - function TestNode(n) {} - RED.nodes.registerType("test-node-mod-1",TestNode); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html deleted file mode 100644 index a1f1b6c79..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js deleted file mode 100644 index d359fb3f2..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/TestNodeModule2.js +++ /dev/null @@ -1,4 +0,0 @@ -// A test node that exports a function -module.exports = function(RED) { - throw new Error("fail to load"); -} diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt deleted file mode 100644 index 59a29af14..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/icons/file.txt +++ /dev/null @@ -1,3 +0,0 @@ -This file exists just to ensure the 'icons' directory is in the repository. -TODO: a future test needs to ensure the right icon files are loaded - this - directory can be used for that diff --git a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json b/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json deleted file mode 100644 index 4f9e46518..000000000 --- a/test/unit/@node-red/registry/lib/resources/local/TestNodeModule/node_modules/VersionMismatchModule/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name" : "VersionMismatchModule", - "version" : "0.0.1", - "description" : "A test node module", - "node-red" : { - "version": "100.0.0", - "nodes": { - "VersionMismatchMod1": "TestNodeModule.js", - "VersionMismatchMod2": "TestNodeModule2.js" - } - } -} diff --git a/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/file.txt b/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/file.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/test_icon.png b/test/unit/@node-red/registry/lib/resources/userDir/lib/icons/test_icon.png deleted file mode 100644 index 4b6b7b5e9b10ec790348eb59f73bdac596e2f3b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1$P6UK?yS)OQjEnx?oJHr&dIz4a@YcVLR^9L z|NsA&-kg6IB%S2#?!wT)D(eB{a29w(76Vni0bxeDQVUa{AbW|YuPggKZdPtVo=;b% zu>pm|JY5_^IIbrr*#Br`l>NUU`M`mYFm{Gnm$g4<{K@tLs$lSR^>bP0l+XkK5O^!s diff --git a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html b/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html deleted file mode 100644 index f38a3a24d..000000000 --- a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - -

    this should be filtered out

    diff --git a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js b/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js deleted file mode 100644 index c31255852..000000000 --- a/test/unit/@node-red/registry/lib/resources/userDir/nodes/TestNode5/TestNode5.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("fail to require"); diff --git a/test/unit/@node-red/registry/lib/subflow_spec.js b/test/unit/@node-red/registry/lib/subflow_spec.js deleted file mode 100644 index 2aa7db203..000000000 --- a/test/unit/@node-red/registry/lib/subflow_spec.js +++ /dev/null @@ -1,3 +0,0 @@ -describe("red/nodes/registry/subflow",function() { - it.skip("NEEDS TESTS"); -}); diff --git a/test/unit/@node-red/registry/lib/util_spec.js b/test/unit/@node-red/registry/lib/util_spec.js deleted file mode 100644 index 7e94d4fe5..000000000 --- a/test/unit/@node-red/registry/lib/util_spec.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -const should = require("should"); -const sinon = require("sinon"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const registryUtil = NR_TEST_UTILS.require("@node-red/registry/lib/util"); - -// Get the internal runtime api -const runtime = NR_TEST_UTILS.require("@node-red/runtime")._; - -const i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("red/nodes/registry/util",function() { - describe("createNodeApi", function() { - let i18n_; - let registerType; - let registerSubflow; - - before(function() { - i18n_ = sinon.stub(i18n,"_").callsFake(function() { - return Array.prototype.slice.call(arguments,0); - }) - registerType = sinon.stub(runtime.nodes,"registerType"); - registerSubflow = sinon.stub(runtime.nodes,"registerSubflow"); - }); - after(function() { - i18n_.restore(); - registerType.restore(); - registerSubflow.restore(); - }) - - it("builds node-specific view of runtime api", function() { - registryUtil.init(runtime); - var result = registryUtil.createNodeApi({id: "my-node", namespace: "my-namespace"}) - // Need a better strategy here. - // For now, validate the node-custom functions - - var message = result._("message"); - // This should prepend the node's namespace to the message - message.should.eql([ 'my-namespace:message' ]); - - var nodeConstructor = () => {}; - var nodeOpts = {}; - result.nodes.registerType("type",nodeConstructor, nodeOpts); - registerType.called.should.be.true(); - registerType.lastCall.args[0].should.eql("my-node") - registerType.lastCall.args[1].should.eql("type") - registerType.lastCall.args[2].should.eql(nodeConstructor) - registerType.lastCall.args[3].should.eql(nodeOpts) - - var subflowDef = {}; - result.nodes.registerSubflow(subflowDef); - registerSubflow.called.should.be.true(); - registerSubflow.lastCall.args[0].should.eql("my-node") - registerSubflow.lastCall.args[1].should.eql(subflowDef) - - }); - }); - describe("checkModuleAllowed", function() { - function checkList(module, version, allowList, denyList) { - return registryUtil.checkModuleAllowed( - module, - version, - registryUtil.parseModuleList(allowList), - registryUtil.parseModuleList(denyList) - ) - } - - it("allows module with no allow/deny list provided", function() { - checkList("abc","1.2.3",[],[]).should.be.true(); - }) - it("defaults allow to * when only deny list is provided", function() { - checkList("abc","1.2.3",["*"],["def"]).should.be.true(); - checkList("def","1.2.3",["*"],["def"]).should.be.false(); - }) - it("uses most specific matching rule", function() { - checkList("abc","1.2.3",["ab*"],["a*"]).should.be.true(); - checkList("def","1.2.3",["d*"],["de*"]).should.be.false(); - }) - it("checks version string using semver rules", function() { - // Deny - checkList("abc","1.2.3",["abc@1.2.2"],["*"]).should.be.false(); - checkList("abc","1.2.3",["abc@1.2.4"],["*"]).should.be.false(); - checkList("abc","1.2.3",["abc@>1.2.3"],["*"]).should.be.false(); - checkList("abc","1.2.3",["abc@>=1.2.3"],["abc"]).should.be.false(); - - - checkList("node-red-contrib-foo","1.2.3",["*"],["*contrib*"]).should.be.false(); - - - // Allow - checkList("abc","1.2.3",["abc@1.2.3"],["*"]).should.be.true(); - checkList("abc","1.2.3",["abc@<1.2.4"],["*"]).should.be.true(); - checkList("abc","1.2.3",["abc"],["abc@>1.2.3"]).should.be.true(); - checkList("abc","1.2.3",["abc"],["abc@<1.2.3||>1.2.3"]).should.be.true(); - checkList("node-red-contrib-foo","1.2.3",["*contrib*"],["*"]).should.be.true(); - }) - - }) -}); diff --git a/test/unit/@node-red/runtime/lib/api/comms_spec.js b/test/unit/@node-red/runtime/lib/api/comms_spec.js deleted file mode 100644 index 0097f5ce8..000000000 --- a/test/unit/@node-red/runtime/lib/api/comms_spec.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var comms = NR_TEST_UTILS.require("@node-red/runtime/lib/api/comms"); -var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); - -describe("runtime-api/comms", function() { - describe("listens for events", function() { - var messages = []; - var clientConnection = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var eventHandlers = {}; - before(function(done) { - sinon.stub(events,"removeListener").callsFake(function() {}) - sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler }) - comms.init({ - log: { - trace: function(){} - } - }) - comms.addConnection({client: clientConnection}).then(done); - }) - after(function(done) { - comms.removeConnection({client: clientConnection}).then(done); - events.removeListener.restore(); - events.on.restore(); - }) - afterEach(function() { - messages = []; - }) - - it('runtime events',function(){ - eventHandlers.should.have.property('runtime-event'); - eventHandlers['runtime-event']({ - id: "my-event", - payload: "my-payload" - }) - messages.should.have.length(1); - messages[0].should.have.property("topic","notification/my-event"); - messages[0].should.have.property("data","my-payload") - }) - it('status events',function(){ - eventHandlers.should.have.property('node-status'); - eventHandlers['node-status']({ - id: "my-event", - status: {text:"my-status",badProperty:"should be filtered"} - }) - messages.should.have.length(1); - messages[0].should.have.property("topic","status/my-event"); - messages[0].should.have.property("data"); - messages[0].data.should.have.property("text","my-status"); - messages[0].data.should.not.have.property("badProperty"); - - }) - it('comms events',function(){ - eventHandlers.should.have.property('runtime-event'); - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(1); - messages[0].should.have.property("topic","my-topic"); - messages[0].should.have.property("data","my-payload") - }) - }); - describe("manages connections", function() { - var eventHandlers = {}; - var messages = []; - var clientConnection1 = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var clientConnection2 = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - before(function() { - sinon.stub(events,"removeListener").callsFake(function() {}) - sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler }) - comms.init({ - log: { - trace: function(){} - } - }) - }) - after(function() { - events.removeListener.restore(); - events.on.restore(); - }) - afterEach(function(done) { - comms.removeConnection({client: clientConnection1}).then(function() { - comms.removeConnection({client: clientConnection2}).then(done); - }); - messages = []; - }) - it('adds new connections',function(done){ - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection1}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(1); - comms.addConnection({client: clientConnection2}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(3); - done(); - }).catch(done); - }); - }); - it('removes connections',function(done){ - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection1}).then(function() { - comms.addConnection({client: clientConnection2}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(2); - comms.removeConnection({client: clientConnection1}).then(function() { - eventHandlers['comms']({ - topic: "my-topic", - data: "my-payload" - }) - messages.should.have.length(3); - done(); - }); - }).catch(done); - }); - }) - }) - - describe("subscriptions", function() { - var messages = []; - var clientConnection = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var clientConnection2 = { - send: function(topic,data) { - messages.push({topic,data}) - } - } - var eventHandlers = {}; - before(function() { - sinon.stub(events,"removeListener").callsFake(function() {}) - sinon.stub(events,"on").callsFake(function(evt,handler) { eventHandlers[evt] = handler }) - comms.init({ - log: { - trace: function(){} - } - }) - }) - after(function() { - events.removeListener.restore(); - events.on.restore(); - }) - afterEach(function(done) { - messages = []; - comms.removeConnection({client: clientConnection}).then(done); - }) - - it('subscribe triggers retained messages',function(done){ - eventHandlers['comms']({ - topic: "my-event", - data: "my-payload", - retain: true - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","my-event"); - messages[0].should.have.property("data","my-payload"); - done(); - }); - }).catch(done); - }) - it('retains non-blank status message',function(done){ - eventHandlers['node-status']({ - id: "node1234", - status: {text:"hello"} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","status/node1234"); - messages[0].should.have.property("data",{text:"hello", fill: undefined, shape: undefined}); - done(); - }); - }).catch(done); - }) - it('does not retain blank status message',function(done){ - eventHandlers['node-status']({ - id: "node1234", - status: {} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - messages.should.have.length(0); - done(); - }); - }).catch(done); - }) - it('does not send blank status if first status',function(done){ - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - eventHandlers['node-status']({ - id: "node5678", - status: {} - }) - messages.should.have.length(0); - done() - }) - }).catch(done); - }); - it('sends blank status if replacing retained',function(done){ - eventHandlers['node-status']({ - id: "node5678", - status: {text:"hello"} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "status/#"}).then(function() { - messages.should.have.length(1); - eventHandlers['node-status']({ - id: "node5678", - status: {} - }) - messages.should.have.length(2); - done() - }) - }).catch(done); - }); - - it('does not retain initial status blank message',function(done){ - eventHandlers['node-status']({ - id: "my-event", - status: {} - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","my-event"); - messages[0].should.have.property("data","my-payload"); - done(); - }); - }).catch(done); - }) - - it('retained messages get cleared',function(done) { - eventHandlers['comms']({ - topic: "my-event", - data: "my-payload", - retain: true - }) - messages.should.have.length(0); - comms.addConnection({client: clientConnection}).then(function() { - return comms.subscribe({client: clientConnection, topic: "my-event"}).then(function() { - messages.should.have.length(1); - messages[0].should.have.property("topic","my-event"); - messages[0].should.have.property("data","my-payload"); - // Now we have a retained message, clear it - eventHandlers['comms']({ - topic: "my-event", - data: "my-payload-cleared" - }); - messages.should.have.length(2); - messages[1].should.have.property("topic","my-event"); - messages[1].should.have.property("data","my-payload-cleared"); - // Now add a second client and subscribe - no message should arrive - return comms.addConnection({client: clientConnection2}).then(function() { - return comms.subscribe({client: clientConnection2, topic: "my-event"}).then(function() { - messages.should.have.length(2); - done(); - }); - }); - }); - }).catch(done); - }); - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/api/context_spec.js b/test/unit/@node-red/runtime/lib/api/context_spec.js deleted file mode 100644 index bf23e9cc6..000000000 --- a/test/unit/@node-red/runtime/lib/api/context_spec.js +++ /dev/null @@ -1,353 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var context = NR_TEST_UTILS.require("@node-red/runtime/lib/api/context"); - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc";} -}); - -var mockContext = function(contents) { - return { - get: function(key,store,callback) { - if (contents.hasOwnProperty(store) && contents[store].hasOwnProperty(key)) { - callback(null,contents[store][key]); - } else { - callback(null,undefined); - } - }, - set: function (key, value, store, callback) { - if (contents.hasOwnProperty(store)) { - if (!value) { - delete contents[store][key]; - callback(null); - } - } else { - callback("err store"); - } - }, - keys: function (store, callback) { - if (contents.hasOwnProperty(store)) { - callback(null, Object.keys(contents[store])); - } else { - callback("err store"); - } - } - }; -}; - -describe("runtime-api/context", function() { - var globalContext, flowContext, nodeContext, contexts; - - beforeEach(function() { - globalContext = { default: { abc: 111 }, file: { abc: 222 } }; - flowContext = { default: { abc: 333 }, file: { abc: 444 } }; - nodeContext = { default: { abc: 555 }, file: { abc: 666 } }; - contexts = { - global: mockContext(globalContext), - flow1: mockContext(flowContext) - }; - context.init({ - nodes: { - listContextStores: function() { - return { default: 'default', stores: [ 'default', 'file' ] }; - }, - getContext: function(id) { - return contexts[id]; - }, - getNode: function(id) { - if (id === 'known') { - return { - context: function() { return mockContext(nodeContext); } - }; - } else { - return null; - } - } - }, - settings: { - functionGlobalContext: { - fgc:1234 - } - }, - log: mockLog() - }); - }); - - describe("getValue", function() { - it('gets global value of default store', function() { - return context.getValue({ - scope: 'global', - id: undefined, - store: undefined, // use default - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','111'); - result.should.have.property('format','number'); - }); - }); - - it('gets global value of specified store', function() { - return context.getValue({ - scope: 'global', - id: undefined, - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','222'); - result.should.have.property('format','number'); - }); - }); - - it('gets flow value of default store', function() { - return context.getValue({ - scope: 'flow', - id: 'flow1', - store: undefined, // use default - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','333'); - result.should.have.property('format','number'); - }); - }); - - it('gets flow value of specified store', function() { - return context.getValue({ - scope: 'flow', - id: 'flow1', - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','444'); - result.should.have.property('format','number'); - }); - }); - - it('gets node value of default store', function() { - return context.getValue({ - scope: 'node', - id: 'known', - store: undefined, // use default - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','555'); - result.should.have.property('format','number'); - }); - }); - - it('gets node value of specified store', function() { - return context.getValue({ - scope: 'node', - id: 'known', - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.have.property('msg','666'); - result.should.have.property('format','number'); - }); - }); - - it('404s for unknown store', function(done) { - context.getValue({ - scope: 'global', - id: undefined, - store: 'unknown', - key: 'abc' - }).then(function(result) { - done("getValue for unknown store should not resolve"); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }); - }); - - it('gets all global value properties', function() { - return context.getValue({ - scope: 'global', - id: undefined, - store: undefined, // use default - key: undefined, // - }).then(function(result) { - result.should.eql({ - default: { abc: { msg: '111', format: 'number' } }, - file: { abc: { msg: '222', format: 'number' } } - }); - }); - }); - - it('gets all flow value properties', function() { - return context.getValue({ - scope: 'flow', - id: 'flow1', - store: undefined, // use default - key: undefined, // - }).then(function(result) { - result.should.eql({ - default: { abc: { msg: '333', format: 'number' } }, - file: { abc: { msg: '444', format: 'number' } } - }); - }); - }); - - it('gets all node value properties', function() { - return context.getValue({ - scope: 'node', - id: 'known', - store: undefined, // use default - key: undefined, // - }).then(function(result) { - result.should.eql({ - default: { abc: { msg: '555', format: 'number' } }, - file: { abc: { msg: '666', format: 'number' } } - }); - }); - }); - - it('gets empty object when specified context doesn\'t exist', function() { - return context.getValue({ - scope: 'node', - id: 'non-existent', - store: 'file', - key: 'abc' - }).then(function(result) { - result.should.be.an.Object(); - result.should.be.empty(); - }); - }); - }); - - describe("delete", function () { - it('deletes global value of default store', function () { - return context.delete({ - scope: 'global', - id: undefined, - store: undefined, // use default - key: 'abc' - }).then(function () { - globalContext.should.eql({ - default: {}, file: { abc: 222 } - }); - }); - }); - - it('deletes global value of specified store', function () { - return context.delete({ - scope: 'global', - id: undefined, - store: 'file', - key: 'abc' - }).then(function () { - globalContext.should.eql({ - default: { abc: 111 }, file: {} - }); - }); - }); - - it('deletes flow value of default store', function () { - return context.delete({ - scope: 'flow', - id: 'flow1', - store: undefined, // use default - key: 'abc' - }).then(function () { - flowContext.should.eql({ - default: {}, file: { abc: 444 } - }); - }); - }); - - it('deletes flow value of specified store', function () { - return context.delete({ - scope: 'flow', - id: 'flow1', - store: 'file', - key: 'abc' - }).then(function () { - flowContext.should.eql({ - default: { abc: 333 }, file: {} - }); - }); - }); - - it('deletes node value of default store', function () { - return context.delete({ - scope: 'node', - id: 'known', - store: undefined, // use default - key: 'abc' - }).then(function () { - nodeContext.should.eql({ - default: {}, file: { abc: 666 } - }); - }); - }); - - it('deletes node value of specified store', function () { - return context.delete({ - scope: 'node', - id: 'known', - store: 'file', - key: 'abc' - }).then(function () { - nodeContext.should.eql({ - default: { abc: 555 }, file: {} - }); - }); - }); - - it('does nothing when specified context doesn\'t exist', function() { - return context.delete({ - scope: 'node', - id: 'non-existent', - store: 'file', - key: 'abc' - }).then(function(result) { - should.not.exist(result); - nodeContext.should.eql({ - default: { abc: 555 }, file: { abc: 666 } - }); - }); - }); - - it('404s for unknown store', function (done) { - context.delete({ - scope: 'global', - id: undefined, - store: 'unknown', - key: 'abc' - }).then(function () { - done("delete for unknown store should not resolve"); - }).catch(function (err) { - err.should.have.property('code', 'not_found'); - err.should.have.property('status', 404); - done(); - }); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/api/flows_spec.js b/test/unit/@node-red/runtime/lib/api/flows_spec.js deleted file mode 100644 index 9062ef52f..000000000 --- a/test/unit/@node-red/runtime/lib/api/flows_spec.js +++ /dev/null @@ -1,430 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/api/flows") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/flows", function() { - describe("getFlows", function() { - it("returns the current flow configuration", function(done) { - flows.init({ - log: mockLog(), - flows: { - getFlows: function() { return [1,2,3] } - } - }); - flows.getFlows({}).then(function(result) { - result.should.eql([1,2,3]); - done(); - }).catch(done); - }); - }); - - describe("setFlows", function() { - var setFlows; - var loadFlows; - var reloadError = false; - beforeEach(function() { - setFlows = sinon.spy(function(flows,credentials,type) { - if (flows[0] === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve("newRev"); - }); - loadFlows = sinon.spy(function() { - if (!reloadError) { - return Promise.resolve("newLoadRev"); - } else { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - }) - flows.init({ - log: mockLog(), - flows: { - getFlows: function() { return {rev:"currentRev",flows:[]} }, - setFlows: setFlows, - loadFlows: loadFlows - } - }) - - }) - it("defaults to full deploy", function(done) { - flows.setFlows({ - flows: {flows:[4,5,6]} - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[2].should.eql("full"); - done(); - }).catch(done); - }); - it("includes credentials when part of the request", function(done) { - flows.setFlows({ - flows: {flows:[4,5,6], credentials: {$:"creds"}}, - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[1].should.eql({$:"creds"}); - setFlows.lastCall.args[2].should.eql("full"); - done(); - }).catch(done); - }); - it("passes through other deploy types", function(done) { - flows.setFlows({ - deploymentType: "nodes", - flows: {flows:[4,5,6]} - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[2].should.eql("nodes"); - done(); - }).catch(done); - }); - it("triggers a flow reload", function(done) { - flows.setFlows({ - deploymentType: "reload" - }).then(function(result) { - result.should.eql({rev:"newLoadRev"}); - setFlows.called.should.be.false(); - loadFlows.called.should.be.true(); - done(); - }).catch(done); - }); - it("allows update when revision matches", function(done) { - flows.setFlows({ - deploymentType: "nodes", - flows: {flows:[4,5,6],rev:"currentRev"} - }).then(function(result) { - result.should.eql({rev:"newRev"}); - setFlows.called.should.be.true(); - setFlows.lastCall.args[0].should.eql([4,5,6]); - setFlows.lastCall.args[2].should.eql("nodes"); - done(); - }).catch(done); - }); - it("rejects update when revision does not match", function(done) { - flows.setFlows({ - deploymentType: "nodes", - flows: {flows:[4,5,6],rev:"notTheCurrentRev"} - }).then(function(result) { - done(new Error("Did not reject rev mismatch")); - }).catch(function(err) { - err.should.have.property('code','version_mismatch'); - err.should.have.property('status',409); - done(); - }).catch(done); - }); - it("rejects when reload fails",function(done) { - reloadError = true; - flows.setFlows({ - deploymentType: "reload" - }).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - done(); - }).catch(done); - }); - it("rejects when update fails",function(done) { - flows.setFlows({ - deploymentType: "full", - flows: {flows:["error",5,6]} - }).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - done(); - }).catch(done); - }); - }); - - describe("addFlow", function() { - var addFlow; - beforeEach(function() { - addFlow = sinon.spy(function(flow) { - if (flow === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve("newId"); - }); - flows.init({ - log: mockLog(), - flows: { - addFlow: addFlow - } - }); - }) - it("adds a flow", function(done) { - flows.addFlow({flow:{a:"123"}}).then(function(id) { - addFlow.called.should.be.true(); - addFlow.lastCall.args[0].should.eql({a:"123"}); - id.should.eql("newId"); - done() - }).catch(done); - }); - it("rejects when add fails", function(done) { - flows.addFlow({flow:"error"}).then(function(id) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - done(); - }).catch(done); - }); - }); - describe("getFlow", function() { - var getFlow; - beforeEach(function() { - getFlow = sinon.spy(function(flow) { - if (flow === "unknown") { - return null; - } - return [1,2,3]; - }); - flows.init({ - log: mockLog(), - flows: { - getFlow: getFlow - } - }); - }) - it("gets a flow", function(done) { - flows.getFlow({id:"123"}).then(function(flow) { - flow.should.eql([1,2,3]); - done() - }).catch(done); - }); - it("rejects when flow not found", function(done) { - flows.getFlow({id:"unknown"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - - describe("updateFlow", function() { - var updateFlow; - beforeEach(function() { - updateFlow = sinon.spy(function(id,flow) { - if (id === "unknown") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = 404; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (id === "error") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve(); - }); - flows.init({ - log: mockLog(), - flows: { - updateFlow: updateFlow - } - }); - }) - it("updates a flow", function(done) { - flows.updateFlow({id:"123",flow:[1,2,3]}).then(function(id) { - id.should.eql("123"); - updateFlow.called.should.be.true(); - updateFlow.lastCall.args[0].should.eql("123"); - updateFlow.lastCall.args[1].should.eql([1,2,3]); - done() - }).catch(done); - }); - it("rejects when flow not found", function(done) { - flows.updateFlow({id:"unknown"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - it("rejects when update fails", function(done) { - flows.updateFlow({id:"error"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - - describe("deleteFlow", function() { - var removeFlow; - beforeEach(function() { - removeFlow = sinon.spy(function(flow) { - if (flow === "unknown") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = 404; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (flow === "error") { - var err = new Error(); - // TODO: quirk of internal api - uses .code for .status - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - return Promise.resolve(); - }); - flows.init({ - log: mockLog(), - flows: { - removeFlow: removeFlow - } - }); - }) - it("deletes a flow", function(done) { - flows.deleteFlow({id:"123"}).then(function() { - removeFlow.called.should.be.true(); - removeFlow.lastCall.args[0].should.eql("123"); - done() - }).catch(done); - }); - it("rejects when flow not found", function(done) { - flows.deleteFlow({id:"unknown"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - it("rejects when delete fails", function(done) { - flows.deleteFlow({id:"error"}).then(function(flow) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getNodeCredentials", function() { - beforeEach(function() { - flows.init({ - log: mockLog(), - nodes: { - getCredentials: function(id) { - if (id === "unknown") { - return undefined; - } else if (id === "known") { - return { - username: "abc", - password: "123" - } - } else if (id === "known2") { - return { - username: "abc", - password: "" - } - } else { - return {}; - } - }, - getCredentialDefinition: function(type) { - if (type === "node") { - return { - username: {type:"text"}, - password: {type:"password"} - } - } else { - return null; - } - } - } - }); - }) - it("returns an empty object for an unknown node", function(done) { - flows.getNodeCredentials({id:"unknown", type:"node"}).then(function(result) { - result.should.eql({}); - done(); - }).catch(done); - }); - it("gets the filtered credentials for a known node with password", function(done) { - flows.getNodeCredentials({id:"known", type:"node"}).then(function(result) { - result.should.eql({ - username: "abc", - has_password: true - }); - done(); - }).catch(done); - }); - it("gets the filtered credentials for a known node without password", function(done) { - flows.getNodeCredentials({id:"known2", type:"node"}).then(function(result) { - result.should.eql({ - username: "abc", - has_password: false - }); - done(); - }).catch(done); - }); - it("gets the empty credentials for a known node without a registered definition", function(done) { - flows.getNodeCredentials({id:"known2", type:"unknown-type"}).then(function(result) { - result.should.eql({}); - done(); - }).catch(done); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/api/index_spec.js b/test/unit/@node-red/runtime/lib/api/index_spec.js deleted file mode 100644 index ecedf22ef..000000000 --- a/test/unit/@node-red/runtime/lib/api/index_spec.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var index = NR_TEST_UTILS.require("@node-red/runtime/lib/api/index"); - - -describe("runtime-api/index", function() { - before(function() { - ["comms","flows","nodes","settings","library","projects"].forEach(n => { - sinon.stub(NR_TEST_UTILS.require(`@node-red/runtime/lib/api/${n}`),"init").callsFake(()=>{}); - }) - }); - after(function() { - ["comms","flows","nodes","settings","library","projects"].forEach(n => { - NR_TEST_UTILS.require(`@node-red/runtime/lib/api/${n}`).init.restore() - }) - }) - it('isStarted', function(done) { - index.init({ - isStarted: ()=>true - }); - index.isStarted({}).then(function(started) { - started.should.be.true(); - done(); - }).catch(done); - }) - - it('isStarted', function(done) { - index.init({ - version: ()=>"1.2.3.4" - }); - index.version({}).then(function(version) { - version.should.eql("1.2.3.4"); - done(); - }).catch(done); - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/api/library_spec.js b/test/unit/@node-red/runtime/lib/api/library_spec.js deleted file mode 100644 index 3fa5291d2..000000000 --- a/test/unit/@node-red/runtime/lib/api/library_spec.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var library = NR_TEST_UTILS.require("@node-red/runtime/lib/api/library") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - -describe("runtime-api/library", function() { - describe("getEntry", function() { - before(function() { - library.init({ - log: mockLog, - library: { - getEntry: function(library, type,path) { - if (type === "known") { - return Promise.resolve("known"); - } else if (type === "forbidden") { - var err = new Error("forbidden"); - err.code = "forbidden"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "not_found") { - var err = new Error("forbidden"); - err.code = "not_found"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "error") { - var err = new Error("error"); - err.code = "unknown_error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "blank") { - return Promise.reject(); - } - } - } - }) - }) - it("returns a known entry", function(done) { - library.getEntry({library: "local",type: "known", path: "/abc"}).then(function(result) { - result.should.eql("known") - done(); - }).catch(done) - }) - it("rejects a forbidden entry", function(done) { - library.getEntry({library: "local",type: "forbidden", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","forbidden"); - err.should.have.property("status",403); - done(); - }).catch(done) - }) - it("rejects an unknown entry", function(done) { - library.getEntry({library: "local",type: "not_found", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","not_found"); - err.should.have.property("status",404); - done(); - }).catch(done) - }) - it("rejects a blank (unknown) entry", function(done) { - library.getEntry({library: "local",type: "blank", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","not_found"); - err.should.have.property("status",404); - done(); - }).catch(done) - }) - it("rejects unexpected error", function(done) { - library.getEntry({library: "local",type: "error", path: "/abc"}).then(function(result) { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("status",400); - done(); - }).catch(done) - }) - }) - describe("saveEntry", function() { - var opts; - before(function() { - library.init({ - log: mockLog, - library: { - saveEntry: function(library,type,path,meta,body) { - opts = {type,path,meta,body}; - if (type === "known") { - return Promise.resolve(); - } else if (type === "forbidden") { - var err = new Error("forbidden"); - err.code = "forbidden"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else if (type === "not_found") { - var err = new Error("forbidden"); - err.code = "not_found"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } - } - } - }) - }) - - it("saves an entry", function(done) { - library.saveEntry({library: "local",type: "known", path: "/abc", meta: {a:1}, body:"123"}).then(function() { - opts.should.have.property("type","known"); - opts.should.have.property("path","/abc"); - opts.should.have.property("meta",{a:1}); - opts.should.have.property("body","123"); - done(); - }).catch(done) - }) - it("rejects a forbidden entry", function(done) { - library.saveEntry({library: "local",type: "forbidden", path: "/abc", meta: {a:1}, body:"123"}).then(function() { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("code","forbidden"); - err.should.have.property("status",403); - done(); - }).catch(done) - }) - it("rejects an unknown entry", function(done) { - library.saveEntry({library: "local",type: "not_found", path: "/abc", meta: {a:1}, body:"123"}).then(function() { - done(new Error("did not reject")); - }).catch(function(err) { - err.should.have.property("status",400); - done(); - }).catch(done) - }) - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/api/nodes_spec.js b/test/unit/@node-red/runtime/lib/api/nodes_spec.js deleted file mode 100644 index 60c6d9edd..000000000 --- a/test/unit/@node-red/runtime/lib/api/nodes_spec.js +++ /dev/null @@ -1,1005 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var nodes = NR_TEST_UTILS.require("@node-red/runtime/lib/api/nodes") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/nodes", function() { - describe("getNodeInfo", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeInfo: function(id) { - if (id === "known") { - return {id:"known"}; - } else { - return null; - } - } - } - }); - }) - it("returns node info", function(done) { - nodes.getNodeInfo({id:"known"}).then(function(result) { - result.should.eql({id:"known"}); - done(); - }).catch(done); - }); - it("returns 404 if node not known", function(done) { - nodes.getNodeInfo({id:"unknown"}).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - describe("getNodeList", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeList: function() { - return [1,2,3]; - } - } - }); - }) - it("returns node list", function(done) { - nodes.getNodeList({}).then(function(result) { - result.should.eql([1,2,3]); - done(); - }).catch(done); - }); - }); - - describe("getNodeConfig", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeConfig: function(id,lang) { - if (id === "known") { - return id+lang; - } else { - return null; - } - } - } - }); - }) - it("returns node config", function(done) { - nodes.getNodeConfig({id:"known",lang:'lang'}).then(function(result) { - result.should.eql("knownlang"); - done(); - }).catch(done); - }); - it("returns 404 if node not known", function(done) { - nodes.getNodeConfig({id:"unknown",lang:'lang'}).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - - describe("getNodeConfigs", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getNodeConfigs: function(lang) { - return lang; - } - } - }); - }) - it("returns all node configs", function(done) { - nodes.getNodeConfigs({lang:'lang'}).then(function(result) { - result.should.eql("lang"); - done(); - }).catch(done); - }); - }); - - describe("getModuleInfo", function() { - beforeEach(function() { - nodes.init({ - log: mockLog(), - nodes: { - getModuleInfo: function(id) { - if (id === "known") { - return {module:"known"}; - } else { - return null; - } - } - } - }); - }) - it("returns node info", function(done) { - nodes.getModuleInfo({module:"known"}).then(function(result) { - result.should.eql({module:"known"}); - done(); - }).catch(done); - }); - it("returns 404 if node not known", function(done) { - nodes.getModuleInfo({module:"unknown"}).then(function(result) { - done(new Error("Did not return internal error")); - }).catch(function(err) { - err.should.have.property('code','not_found'); - err.should.have.property('status',404); - done(); - }).catch(done); - }); - }); - - describe.skip("addModule", function() {}); - describe.skip("removeModule", function() {}); - describe.skip("setModuleState", function() {}); - describe.skip("setNodeSetState", function() {}); - - describe.skip("getModuleCatalogs", function() {}); - describe.skip("getModuleCatalog", function() {}); - - describe.skip("getIconList", function() {}); - describe.skip("getIcon", function() {}); - - -}); - -/* -var should = require("should"); -var request = require('supertest'); -var express = require('express'); -var bodyParser = require('body-parser'); -var sinon = require('sinon'); - -var nodes = require("../../../../red/api/admin/nodes"); -var apiUtil = require("../../../../red/api/util"); - -describe("api/admin/nodes", function() { - - var app; - function initNodes(runtime) { - runtime.log = { - audit:function(e){},//console.log(e)}, - _:function(){}, - info: function(){}, - warn: function(){} - } - runtime.events = { - emit: function(){} - } - nodes.init(runtime); - - } - - before(function() { - app = express(); - app.use(bodyParser.json()); - app.get("/nodes",nodes.getAll); - app.post("/nodes",nodes.post); - app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.getModule); - app.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.getSet); - app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,nodes.putModule); - app.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,nodes.putSet); - app.get("/getIcons",nodes.getIcons); - app.delete("/nodes/:id",nodes.delete); - sinon.stub(apiUtil,"determineLangFromHeaders").callsFake(function() { - return "en-US"; - }); - }); - after(function() { - apiUtil.determineLangFromHeaders.restore(); - }) - - describe('get nodes', function() { - it('returns node list', function(done) { - initNodes({ - nodes:{ - getNodeList: function() { - return [1,2,3]; - } - } - }); - request(app) - .get('/nodes') - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.be.an.Array(); - res.body.should.have.lengthOf(3); - done(); - }); - }); - - it('returns node configs', function(done) { - initNodes({ - nodes:{ - getNodeConfigs: function() { - return ""; - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns node module info', function(done) { - initNodes({ - nodes:{ - getModuleInfo: function(id) { - return {"node-red":{name:"node-red"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-red') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - done(); - }); - }); - - it('returns 404 for unknown module', function(done) { - initNodes({ - nodes:{ - getModuleInfo: function(id) { - return {"node-red":{name:"node-red"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-blue') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns individual node info', function(done) { - initNodes({ - nodes:{ - getNodeInfo: function(id) { - return {"node-red/123":{id:"node-red/123"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'application/json') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","node-red/123"); - done(); - }); - }); - - it('returns individual node configs', function(done) { - initNodes({ - nodes:{ - getNodeConfig: function(id) { - return {"node-red/123":""}[id]; - } - }, - i18n: { - determineLangFromHeaders: function(){} - } - }); - request(app) - .get('/nodes/node-red/123') - .set('Accept', 'text/html') - .expect(200) - .expect("") - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 404 for unknown node', function(done) { - initNodes({ - nodes:{ - getNodeInfo: function(id) { - return {"node-red/123":{id:"node-red/123"}}[id]; - } - } - }); - request(app) - .get('/nodes/node-red/456') - .set('Accept', 'application/json') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - }); - - describe('install', function() { - - it('returns 400 if settings are unavailable', function(done) { - initNodes({ - settings:{available:function(){return false}} - }); - request(app) - .post('/nodes') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 400 if request is invalid', function(done) { - initNodes({ - settings:{available:function(){return true}} - }); - request(app) - .post('/nodes') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - describe('by module', function() { - it('installs the module and returns module info', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null; }, - installModule: function() { - return Promise.resolve({ - name:"foo", - nodes:[{id:"123"}] - }); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","foo"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("id","123"); - done(); - }); - }); - - it('fails the install if already installed', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return {nodes:{id:"123"}}; }, - installModule: function() { - return Promise.resolve({id:"123"}); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('fails the install if module error', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null }, - installModule: function() { - return Promise.reject(new Error("test error")); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Error: test error"); - done(); - }); - }); - it('fails the install if module not found', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null }, - installModule: function() { - var err = new Error("test error"); - err.code = 404; - return Promise.reject(err); - } - } - }); - request(app) - .post('/nodes') - .send({module: 'foo'}) - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - }); - }); - describe('delete', function() { - it('returns 400 if settings are unavailable', function(done) { - initNodes({ - settings:{available:function(){return false}} - }); - - request(app) - .del('/nodes/123') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - describe('by module', function() { - it('uninstalls the module', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return {nodes:[{id:"123"}]} }, - getNodeInfo: function() { return null }, - uninstallModule: function() { return Promise.resolve({id:"123"});} - } - }); - request(app) - .del('/nodes/foo') - .expect(204) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('fails the uninstall if the module is not installed', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null }, - getNodeInfo: function() { return null } - } - }); - request(app) - .del('/nodes/foo') - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('fails the uninstall if the module is not installed', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return {nodes:[{id:"123"}]} }, - getNodeInfo: function() { return null }, - uninstallModule: function() { return Promise.reject(new Error("test error"));} - } - }); - request(app) - .del('/nodes/foo') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Error: test error"); - done(); - }); - }); - }); - - }); - - describe('enable/disable', function() { - it('returns 400 if settings are unavailable', function(done) { - initNodes({ - settings:{available:function(){return false}} - }); - request(app) - .put('/nodes/123') - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 400 for invalid node payload', function(done) { - initNodes({ - settings:{available:function(){return true}} - }); - request(app) - .put('/nodes/node-red/foo') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Invalid request"); - done(); - }); - }); - - it('returns 400 for invalid module payload', function(done) { - initNodes({ - settings:{available:function(){return true}} - }); - request(app) - .put('/nodes/foo') - .send({}) - .expect(400) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("message","Invalid request"); - - done(); - }); - }); - - it('returns 404 for unknown node', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return null } - } - }); - - request(app) - .put('/nodes/node-red/foo') - .send({enabled:false}) - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('returns 404 for unknown module', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function(id) { return null } - } - }); - - request(app) - .put('/nodes/node-blue') - .send({enabled:false}) - .expect(404) - .end(function(err,res) { - if (err) { - throw err; - } - done(); - }); - }); - - it('enables disabled node', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: false} }, - enableNode: function() { return Promise.resolve({id:"123",enabled: true,types:['a']}); } - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",true); - - done(); - }); - }); - - it('disables enabled node', function(done) { - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: true} }, - disableNode: function() { return Promise.resolve({id:"123",enabled: false,types:['a']}); } - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:false}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",false); - - done(); - }); - }); - - describe('no-ops if already in the right state', function() { - function run(state,done) { - var enableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: true,types:['a']}) }); - var disableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: false,types:['a']}) }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: state} }, - enableNode: enableNode, - disableNode: disableNode - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.false(); - disableNodeCalled.should.be.false(); - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - - describe('does not no-op if err on node', function() { - function run(state,done) { - var enableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: true,types:['a']}) }); - var disableNode = sinon.spy(function() { return Promise.resolve({id:"123",enabled: false,types:['a']}) }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getNodeInfo: function() { return {id:"123",enabled: state, err:"foo"} }, - enableNode: enableNode, - disableNode: disableNode - } - }); - request(app) - .put('/nodes/node-red/foo') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.equal(state); - disableNodeCalled.should.be.equal(!state); - res.body.should.have.property("id","123"); - res.body.should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - - it('enables disabled module', function(done) { - var n1 = {id:"123",enabled:false,types:['a']}; - var n2 = {id:"456",enabled:false,types:['b']}; - var enableNode = sinon.stub(); - enableNode.onFirstCall().returns((function() { - n1.enabled = true; - return Promise.resolve(n1); - })()); - enableNode.onSecondCall().returns((function() { - n2.enabled = true; - return Promise.resolve(n2); - })()); - enableNode.returns(null); - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} }, - enableNode: enableNode - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:true}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",true); - res.body.nodes[1].should.have.property("enabled",true); - - done(); - }); - }); - - it('disables enabled module', function(done) { - var n1 = {id:"123",enabled:true,types:['a']}; - var n2 = {id:"456",enabled:true,types:['b']}; - var disableNode = sinon.stub(); - disableNode.onFirstCall().returns((function() { - n1.enabled = false; - return Promise.resolve(n1); - })()); - disableNode.onSecondCall().returns((function() { - n2.enabled = false; - return Promise.resolve(n2); - })()); - disableNode.returns(null); - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[n1, n2]} }, - disableNode: disableNode - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:false}) - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",false); - res.body.nodes[1].should.have.property("enabled",false); - - done(); - }); - }); - - describe('no-ops if a node in module already in the right state', function() { - function run(state,done) { - var node = {id:"123",enabled:state,types:['a']}; - var enableNode = sinon.spy(function(id) { - node.enabled = true; - return Promise.resolve(node); - }); - var disableNode = sinon.spy(function(id) { - node.enabled = false; - return Promise.resolve(node); - }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[node]}; }, - enableNode: enableNode, - disableNode: disableNode - } - }); - request(app) - .put('/nodes/node-red') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.false(); - disableNodeCalled.should.be.false(); - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - - describe('does not no-op if err on a node in module', function() { - function run(state,done) { - var node = {id:"123",enabled:state,types:['a'],err:"foo"}; - var enableNode = sinon.spy(function(id) { - node.enabled = true; - return Promise.resolve(node); - }); - var disableNode = sinon.spy(function(id) { - node.enabled = false; - return Promise.resolve(node); - }); - - initNodes({ - settings:{available:function(){return true}}, - nodes:{ - getModuleInfo: function() { return {name:"node-red", nodes:[node]}; }, - enableNode: enableNode, - disableNode: disableNode - } - }); - - request(app) - .put('/nodes/node-red') - .send({enabled:state}) - .expect(200) - .end(function(err,res) { - var enableNodeCalled = enableNode.called; - var disableNodeCalled = disableNode.called; - if (err) { - throw err; - } - enableNodeCalled.should.be.equal(state); - disableNodeCalled.should.be.equal(!state); - res.body.should.have.property("name","node-red"); - res.body.should.have.property("nodes"); - res.body.nodes[0].should.have.property("enabled",state); - - done(); - }); - } - it('already enabled', function(done) { - run(true,done); - }); - it('already disabled', function(done) { - run(false,done); - }); - }); - }); - - describe('get icons', function() { - it('returns icon list', function(done) { - initNodes({ - nodes:{ - getNodeIcons: function() { - return {"module":["1.png","2.png","3.png"]}; - } - } - }); - request(app) - .get('/getIcons') - .expect(200) - .end(function(err,res) { - if (err) { - throw err; - } - console.log(res.body); - res.body.should.have.property("module"); - res.body.module.should.be.an.Array(); - res.body.module.should.have.lengthOf(3); - done(); - }); - }); - }); -}); - -*/ diff --git a/test/unit/@node-red/runtime/lib/api/plugins_spec.js b/test/unit/@node-red/runtime/lib/api/plugins_spec.js deleted file mode 100644 index 7ae6d8286..000000000 --- a/test/unit/@node-red/runtime/lib/api/plugins_spec.js +++ /dev/null @@ -1,68 +0,0 @@ -const should = require("should"); -const sinon = require("sinon"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/api/plugins") - -const mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/plugins", function() { - const pluginList = [{id:"one",module:'test-module'},{id:"two",module:"node-red"}]; - const pluginConfigs = "123"; - - describe("getPluginList", function() { - it("gets the plugin list", function() { - plugins.init({ - log: mockLog(), - plugins: { - getPluginList: function() { return pluginList} - } - }); - return plugins.getPluginList({}).then(function(result) { - result.should.eql(pluginList); - }) - }); - }); - describe("getPluginConfigs", function() { - it("gets the plugin configs", function() { - plugins.init({ - log: mockLog(), - plugins: { - getPluginConfigs: function() { return pluginConfigs} - } - }); - return plugins.getPluginConfigs({}).then(function(result) { - result.should.eql(pluginConfigs); - }) - }); - }); - describe("getPluginCatalogs", function() { - it("gets the plugin catalogs", function() { - plugins.init({ - log: mockLog(), - plugins: { - getPluginList: function() { return pluginList} - }, - i18n: { - i: { - changeLanguage: function(lang,done) { done && done() }, - getResourceBundle: function(lang, id) { return {lang,id}} - } - } - }); - return plugins.getPluginCatalogs({lang: "en-US"}).then(function(result) { - JSON.stringify(result).should.eql(JSON.stringify({ one: { lang: "en-US", id: "one" } })) - }) - }); - }); - -}); \ No newline at end of file diff --git a/test/unit/@node-red/runtime/lib/api/projects_spec.js b/test/unit/@node-red/runtime/lib/api/projects_spec.js deleted file mode 100644 index 2fe7ca221..000000000 --- a/test/unit/@node-red/runtime/lib/api/projects_spec.js +++ /dev/null @@ -1,1170 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var projects = NR_TEST_UTILS.require("@node-red/runtime/lib/api/projects") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/settings", function() { - describe("available", function() { - it("resolves true if projects available", function(done) { - projects.init({ - storage: { - projects: {} - } - }); - projects.available().then(function(result) { - result.should.be.true(); - done(); - }).catch(done); - }) - it("resolves false if projects unavailable", function(done) { - projects.init({ - storage: { - } - }); - projects.available().then(function(result) { - result.should.be.false(); - done(); - }).catch(done); - }) - - }); - describe("listProjects", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - listProjects: sinon.spy(function(user) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve([1,2,3]); - } - }), - getActiveProject: function(user) { - if (user === "noActive") { - return null; - } - return {name:"aProject"}; - } - }}}; - before(function() { - projects.init(runtime); - }) - it("lists the projects, without an active project", function(done) { - projects.listProjects({user:"noActive"}).then(function(result) { - result.should.have.property('projects',[1,2,3]); - result.should.not.have.property('active'); - done(); - }).catch(done); - }); - it("lists the projects, with an active project", function(done) { - projects.listProjects({user:"foo"}).then(function(result) { - result.should.have.property('projects',[1,2,3]); - result.should.have.property('active','aProject'); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.listProjects({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - err.should.have.property('status',400); - done(); - }).catch(done); - }); - - }); - describe("createProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - createProject: sinon.spy(function(user,project) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve(project); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("create project", function(done) { - projects.createProject({user:"known",project:{a:1}}).then(function(result) { - result.should.eql({a:1}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.createProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("initialiseProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - initialiseProject: sinon.spy(function(user,id,project) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({id,project}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("intialise project", function(done) { - projects.initialiseProject({user:"known",id:123, project:{a:1}}).then(function(result) { - result.should.eql({id:123, project:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.initialiseProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getActiveProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getActiveProject: sinon.spy(function(user) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve("123"); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("returns active project", function(done) { - projects.getActiveProject({user:"known"}).then(function(result) { - result.should.eql("123"); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getActiveProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("setActiveProject", function() { - var activeProject; - var runtime; - beforeEach(function() { - runtime = { - log: mockLog(), - storage: {projects: { - getActiveProject: sinon.spy(function() { return activeProject;}), - setActiveProject: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - projects.init(runtime); - }) - it("sets project if current project is unset", function(done) { - activeProject = null; - projects.setActiveProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("sets project if current project is different", function(done) { - activeProject = {name:456}; - projects.setActiveProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("no-ops if current project is same", function(done) { - activeProject = {name:123}; - projects.setActiveProject({user:"known",id:123}).then(function(result) { - (result === undefined).should.be.true(); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.setActiveProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getProject: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("returns project", function(done) { - projects.getProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getProject({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("updateProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - updateProject: sinon.spy(function(user,id,project) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,project}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("updates project", function(done) { - projects.updateProject({user:"known",id:123,project:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,project:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.updateProject({user:"error",id:123,project:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("deleteProject", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - deleteProject: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("deletes project", function(done) { - projects.deleteProject({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.deleteProject({user:"error",id:123}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getStatus", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getStatus: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets status", function(done) { - projects.getStatus({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getStatus({user:"error",id:123,project:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("getBranches", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getBranches: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets branches", function(done) { - projects.getBranches({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getBranches({user:"error",id:123,remote:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("getBranchStatus", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getBranchStatus: sinon.spy(function(user,id,branch) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,branch}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets branch status", function(done) { - projects.getBranchStatus({user:"known",id:123,branch:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,branch:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getBranchStatus({user:"error",id:123,branch:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("setBranch", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - setBranch: sinon.spy(function(user,id,branch,create) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,branch,create}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("commits", function(done) { - projects.setBranch({user:"known",id:123,branch:{a:1},create:true}).then(function(result) { - result.should.eql({user:"known",id:123,branch:{a:1},create:true}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.setBranch({user:"error",id:123,branch:{a:1},create:true}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("deleteBranch", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - deleteBranch: sinon.spy(function(user,id,branch,something,force) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,branch,force}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("commits", function(done) { - projects.deleteBranch({user:"known",id:123,branch:{a:1},force:true}).then(function(result) { - result.should.eql({user:"known",id:123,branch:{a:1},force:true}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.deleteBranch({user:"error",id:123,branch:{a:1},force:true}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("commit", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - commit: sinon.spy(function(user,id,message) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,message}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("commits", function(done) { - projects.commit({user:"known",id:123,message:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,message:{message:{a:1}}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.commit({user:"error",id:123,message:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("getCommit", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getCommit: sinon.spy(function(user,id,sha) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,sha}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets commit", function(done) { - projects.getCommit({user:"known",id:123,sha:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,sha:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getCommit({user:"error",id:123,sha:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getCommits", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getCommits: sinon.spy(function(user,id,options) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,options}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets commits with default limit/before", function(done) { - projects.getCommits({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123,options:{limit:20,before:undefined}}); - done(); - }).catch(done); - }); - it("gets commits with provided limit/before", function(done) { - projects.getCommits({user:"known",id:123,limit:10,before:456}).then(function(result) { - result.should.eql({user:"known",id:123,options:{limit:10,before:456}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getCommits({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("abortMerge", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - abortMerge: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("aborts merge", function(done) { - projects.abortMerge({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.abortMerge({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("resolveMerge", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - resolveMerge: sinon.spy(function(user,id,path,resolution) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path,resolution}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("resolves merge", function(done) { - projects.resolveMerge({user:"known",id:123,path:"/abc",resolution:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:"/abc",resolution:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.resolveMerge({user:"error",id:123,path:"/abc",resolution:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getFiles", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getFiles: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets files", function(done) { - projects.getFiles({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getFiles({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getFile: sinon.spy(function(user,id,path,tree) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path,tree}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets file", function(done) { - projects.getFile({user:"known",id:123,path:"/abc",tree:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:"/abc",tree:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getFile({user:"error",id:123,path:"/abc",tree:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("stageFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - stageFile: sinon.spy(function(user,id,path) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("stages a file", function(done) { - projects.stageFile({user:"known",id:123,path:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.stageFile({user:"error",id:123,path:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("unstageFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - unstageFile: sinon.spy(function(user,id,path) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("unstages a file", function(done) { - projects.unstageFile({user:"known",id:123,path:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.unstageFile({user:"error",id:123,path:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("revertFile", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - revertFile: sinon.spy(function(user,id,path) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("reverts a file", function(done) { - projects.revertFile({user:"known",id:123,path:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.revertFile({user:"error",id:123,path:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getFileDiff", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getFileDiff: sinon.spy(function(user,id,path,type) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,path,type}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets file diff", function(done) { - projects.getFileDiff({user:"known",id:123,path:{a:1},type:"abc"}).then(function(result) { - result.should.eql({user:"known",id:123,path:{a:1},type:"abc"}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getFileDiff({user:"error",id:123,path:{a:1},type:"abc"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("getRemotes", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - getRemotes: sinon.spy(function(user,id) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("gets remotes", function(done) { - projects.getRemotes({user:"known",id:123}).then(function(result) { - result.should.eql({user:"known",id:123}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.getRemotes({user:"error"}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("addRemote", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - addRemote: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("adds a remote", function(done) { - projects.addRemote({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.addRemote({user:"error",id:123,remote:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("removeRemote", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - removeRemote: sinon.spy(function(user,id,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("removes a remote", function(done) { - projects.removeRemote({user:"known",id:123,remote:{a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,remote:{a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.removeRemote({user:"error",id:123,remote:{a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - describe("updateRemote", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - updateRemote: sinon.spy(function(user,id,name,remote) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,name,remote}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("updates a remote", function(done) { - projects.updateRemote({user:"known",id:123,remote:{name:"abc",a:1}}).then(function(result) { - result.should.eql({user:"known",id:123,name:"abc",remote:{name:"abc",a:1}}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.updateRemote({user:"error",id:123,remote:{name:"abc",a:1}}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("pull", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - pull: sinon.spy(function(user,id,remote,track,allowUnrelatedHistories) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote,track,allowUnrelatedHistories}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("pulls", function(done) { - projects.pull({user:"known",id:123,remote:"abc",track:false,allowUnrelatedHistories:true}).then(function(result) { - result.should.eql({user:"known",id:123,remote:"abc",track:false,allowUnrelatedHistories:true}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.pull({user:"error",id:123,remote:"abc",track:false,allowUnrelatedHistories:true}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - describe("push", function() { - var runtime = { - log: mockLog(), - storage: {projects: { - push: sinon.spy(function(user,id,remote,track) { - if (user === "error") { - var err = new Error("error"); - err.code = "error"; - var p = Promise.reject(err); - p.catch(()=>{}); - return p; - } else { - return Promise.resolve({user,id,remote,track}); - } - }) - }}}; - before(function() { - projects.init(runtime); - }) - it("pulls", function(done) { - projects.push({user:"known",id:123,remote:"abc",track:false}).then(function(result) { - result.should.eql({user:"known",id:123,remote:"abc",track:false}); - done(); - }).catch(done); - }); - it("rejects with internal error", function(done) { - projects.push({user:"error",id:123,remote:"abc",track:false}).then(function(result) { - done(new Error("Did not reject internal error")); - }).catch(function(err) { - err.should.have.property('code','error'); - // err.should.have.property('status',400); - done(); - }).catch(done); - }); - }); - - -}); diff --git a/test/unit/@node-red/runtime/lib/api/settings_spec.js b/test/unit/@node-red/runtime/lib/api/settings_spec.js deleted file mode 100644 index 9b3b94229..000000000 --- a/test/unit/@node-red/runtime/lib/api/settings_spec.js +++ /dev/null @@ -1,988 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require("sinon"); -var clone = require("clone"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/api/settings") - -var mockLog = () => ({ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -}) - -describe("runtime-api/settings", function() { - describe("getRuntimeSettings", function() { - it("gets the runtime settings", function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => ["lib1"] }, - storage: {} - }) - return settings.getRuntimeSettings({}).then(result => { - result.should.have.property("httpNodeRoot","testHttpNodeRoot"); - result.should.have.property("version","testVersion"); - result.should.have.property("paletteCategories",["red","blue","green"]); - result.should.have.property("libraries",["lib1"]); - result.should.have.property("testNodeSetting","helloWorld"); - result.should.have.property("testPluginSettings","helloPluginWorld"); - result.should.not.have.property("foo",123); - result.should.have.property("flowEncryptionType","test-key-type"); - result.should.not.have.property("user"); - result.should.have.property("externalModules"); - result.externalModules.should.eql({palette:{allowInstall:false, allowUpload: false}}); - - }) - }); - it("gets the filtered user settings", function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: {} - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.have.property("user"); - result.user.should.have.property("username","nick"); - result.user.should.have.property("permissions","*"); - result.user.should.have.property("image","http://example.com"); - result.user.should.have.property("anonymous",false); - result.user.should.not.have.property("private"); - }) - }); - it("gets the filtered settings when editor disabled ", function() { - settings.init({ - settings: { - disableEditor: true, - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - getActiveProject: () => 'test-active-project', - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.have.property("user"); - result.user.should.have.property("username","nick"); - result.user.should.have.property("permissions","*"); - result.user.should.have.property("image","http://example.com"); - result.user.should.have.property("anonymous",false); - result.user.should.not.have.property("private"); - - // Filtered out when disableEditor is true - result.should.not.have.property("paletteCategories",["red","blue","green"]); - result.should.not.have.property("testNodeSetting","helloWorld"); - result.should.not.have.property("foo",123); - result.should.not.have.property("flowEncryptionType","test-key-type"); - result.should.not.have.property("project"); - result.should.not.have.property("git"); - - }) - }); - it('includes project settings if projects available', function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - getActiveProject: () => 'test-active-project', - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.have.property("project","test-active-project"); - result.should.not.have.property("files"); - result.should.have.property("git"); - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - }); - }); - - it('includes existing files details if projects enabled but no active project and files exist', function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - flowFileExists: () => true, - getActiveProject: () => false, - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - result.should.not.have.property("project"); - result.should.have.property("files"); - result.files.should.have.property("flow",'test-flow-file'); - result.files.should.have.property("credentials",'test-creds-file'); - result.should.have.property("git"); - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - }); - }); - - it('does not include file details if projects enabled but no active project and files do not exist', function() { - settings.init({ - settings: { - foo: 123, - httpNodeRoot: "testHttpNodeRoot", - version: "testVersion", - paletteCategories :["red","blue","green"], - exportNodeSettings: (obj) => { - obj.testNodeSetting = "helloWorld"; - }, - }, - plugins: { - exportPluginSettings: (obj) => { - obj.testPluginSettings = "helloPluginWorld"; - } - }, - nodes: { - listContextStores: () => { return {stores:["file","memory"], default: "file"} }, - installerEnabled: () => false, - getCredentialKeyType: () => "test-key-type" - }, - library: {getLibraries: () => { ["lib1"]} }, - storage: { - projects: { - flowFileExists: () => false, - getActiveProject: () => false, - getFlowFilename: () => 'test-flow-file', - getCredentialsFilename: () => 'test-creds-file', - getGlobalGitUser: () => {return {name:'foo',email:'foo@example.com'}} - } - } - }) - return settings.getRuntimeSettings({ - user: { - username: "nick", - anonymous: false, - image: "http://example.com", - permissions: "*", - private: "secret" - } - }).then(result => { - result.should.not.have.property("project"); - result.should.not.have.property("files"); - result.should.have.property("git"); - result.git.should.have.property("globalUser",{name:'foo',email:'foo@example.com'}); - }); - }); - }); - describe("getUserSettings", function() { - before(function() { - settings.init({ - settings: { - getUserSettings: username => username - } - }); - }) - it("returns default user settings", function() { - return settings.getUserSettings({}).then(result => { - result.should.eql("_"); - }) - }) - it("returns default user settings for anonymous", function() { - return settings.getUserSettings({user:{anonymous:true}}).then(result => { - result.should.eql("_"); - }) - }) - it("returns user settings", function() { - return settings.getUserSettings({user:{username:'nick'}}).then(result => { - result.should.eql("nick"); - }) - }) - }); - - describe("updateUserSettings", function() { - var userSettings; - before(function() { - settings.init({ - settings: { - getUserSettings: username => clone(userSettings[username]), - setUserSettings: (username, settings) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } else if (username === 'throw') { - throw new Error("thrown error"); - } - userSettings[username] = clone(settings); - return Promise.resolve(); - } - }, - log: mockLog() - }); - }) - beforeEach(function() { - userSettings = { - "_": { abc: 123 }, - "nick": {abc: 456} - } - }) - it('sets default user settings', function() { - return settings.updateUserSettings({settings:{abc:789}}).then(function() { - userSettings._.abc.should.eql(789) - }) - }) - it('merges user settings', function() { - return settings.updateUserSettings({settings:{def:789}}).then(function() { - userSettings._.abc.should.eql(123) - userSettings._.def.should.eql(789) - }) - }) - it('sets default user settings for anonymous user', function() { - return settings.updateUserSettings({user:{anonymous:true},settings:{def:789}}).then(function() { - userSettings._.abc.should.eql(123) - userSettings._.def.should.eql(789) - }) - }) - it('sets named user settings', function() { - return settings.updateUserSettings({user:{username:'nick'},settings:{def:789}}).then(function() { - userSettings.nick.abc.should.eql(456) - userSettings.nick.def.should.eql(789) - }) - }) - it('rejects with suitable error', function(done) { - settings.updateUserSettings({user:{username:'error'},settings:{def:789}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - it('rejects with suitable error - thrown', function(done) { - settings.updateUserSettings({user:{username:'throw'},settings:{def:789}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - describe("getUserKeys", function() { - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - listSSHKeys: username => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } - return Promise.resolve([username]) - } - } - } - } - }) - }) - it('returns the default users keys', function() { - return settings.getUserKeys({}).then(result => { - result.should.eql(['__default']); - }) - }) - it('returns the default users keys for anonymous', function() { - return settings.getUserKeys({user:{anonymous:true}}).then(result => { - result.should.eql(['__default']); - }) - }) - it('returns the users keys', function() { - return settings.getUserKeys({user:{username:'nick'}}).then(result => { - result.should.eql(['nick']); - }) - }) - it('rejects with suitable error', function(done) { - settings.getUserKeys({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - - describe("getUserKey", function() { - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - getSSHKey: (username, id) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } else if (username === '404') { - return Promise.resolve(null); - } - return Promise.resolve({username,id}) - } - } - } - } - }) - }) - it('returns the default user key', function() { - return settings.getUserKey({id:'keyid'}).then(result => { - result.should.eql({id:'keyid',username:"__default"}); - }) - }) - it('returns the default user key - anonymous', function() { - return settings.getUserKey({user:{anonymous:true},id:'keyid'}).then(result => { - result.should.eql({id:'keyid',username:"__default"}); - }) - }) - it('returns the user key', function() { - return settings.getUserKey({user:{username:'nick'},id:'keyid'}).then(result => { - result.should.eql({id:'keyid',username:"nick"}); - }) - }) - it('404s for unknown key', function(done) { - settings.getUserKey({user:{username:'404'},id:'keyid'}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 404); - err.should.have.property('code', 'not_found'); - done(); - }).catch(done); - }) - it('rejects with suitable error', function(done) { - settings.getUserKey({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - describe("generateUserKey", function() { - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - generateSSHKey: (username, opts) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } - return Promise.resolve(JSON.stringify({username,opts})) - } - } - } - } - }) - }) - it('generates for the default user', function() { - return settings.generateUserKey({id:'keyid'}).then(result => { - var data = JSON.parse(result); - data.should.eql({opts:{id:'keyid'},username:"__default"}); - }) - }) - it('generates for the default user - anonymous', function() { - return settings.generateUserKey({user:{anonymous:true},id:'keyid'}).then(result => { - var data = JSON.parse(result); - data.should.eql({opts:{user:{anonymous:true},id:'keyid'},username:"__default"}); - }) - }) - it('generates for the user', function() { - return settings.generateUserKey({user:{username:'nick'},id:'keyid'}).then(result => { - var data = JSON.parse(result); - data.should.eql({opts:{user:{username:'nick'},id:'keyid'},username:"nick"}); - }) - }) - it('rejects with suitable error', function(done) { - settings.generateUserKey({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - - }); - describe("removeUserKey", function() { - var received = {}; - before(function() { - settings.init({ - storage: { - projects: { - ssh: { - deleteSSHKey: (username, id) => { - if (username === 'error') { - var p = Promise.reject(new Error("unknown user")); - p.catch(()=>{}); - return p; - } - received.username = username; - received.id = id; - return Promise.resolve(); - } - } - } - } - }) - }); - beforeEach(function() { - received.username = ""; - received.id = ""; - }) - it('removes for the default user', function() { - return settings.removeUserKey({id:'keyid'}).then(() => { - received.username.should.eql("__default"); - received.id.should.eql("keyid"); - }) - }) - it('removes for the default user key - anonymous', function() { - return settings.removeUserKey({user:{anonymous:true},id:'keyid'}).then(() => { - received.username.should.eql("__default"); - received.id.should.eql("keyid"); - }) - }) - it('returns the user key', function() { - return settings.removeUserKey({user:{username:'nick'},id:'keyid'}).then(() => { - received.username.should.eql("nick"); - received.id.should.eql("keyid"); - }) - }) - it('rejects with suitable error', function(done) { - settings.removeUserKey({user:{username:'error'}}).then(result => { - done("Unexpected resolve for error case"); - }).catch(err => { - err.should.have.property('status', 400); - done(); - }).catch(done); - }) - }); - -}); - - - -/* - - -var should = require("should"); -var sinon = require("sinon"); -var request = require("supertest"); -var express = require("express"); -var editorApi = require("../../../../red/api/editor"); -var comms = require("../../../../red/api/editor/comms"); -var info = require("../../../../red/api/editor/settings"); -var auth = require("../../../../red/api/auth"); -var sshkeys = require("../../../../red/api/editor/sshkeys"); -var bodyParser = require("body-parser"); -var fs = require("fs-extra"); -var fspath = require("path"); - - -describe("api/editor/sshkeys", function() { - var app; - var mockList = [ - 'library','theme','locales','credentials','comms' - ] - var isStarted = true; - var errors = []; - var session_data = {}; - - var mockRuntime = { - settings:{ - httpNodeRoot: true, - httpAdminRoot: true, - disableEditor: false, - exportNodeSettings:function(){}, - storage: { - getSessions: function(){ - return Promise.resolve(session_data); - }, - setSessions: function(_session) { - session_data = _session; - return Promise.resolve(); - } - } - }, - log:{audit:function(){},error:function(msg){errors.push(msg)},trace:function(){}}, - storage: { - projects: { - ssh: { - init: function(){}, - listSSHKeys: function(){}, - getSSHKey: function(){}, - generateSSHKey: function(){}, - deleteSSHKey: function(){} - } - } - }, - events:{on:function(){},removeListener:function(){}}, - isStarted: function() { return isStarted; }, - nodes: {installerEnabled: function() { return false }} - }; - - before(function() { - auth.init(mockRuntime); - app = express(); - app.use(bodyParser.json()); - app.use(editorApi.init({},mockRuntime)); - }); - after(function() { - }) - - beforeEach(function() { - sinon.stub(mockRuntime.storage.projects.ssh, "listSSHKeys"); - sinon.stub(mockRuntime.storage.projects.ssh, "getSSHKey"); - sinon.stub(mockRuntime.storage.projects.ssh, "generateSSHKey"); - sinon.stub(mockRuntime.storage.projects.ssh, "deleteSSHKey"); - }) - afterEach(function() { - mockRuntime.storage.projects.ssh.listSSHKeys.restore(); - mockRuntime.storage.projects.ssh.getSSHKey.restore(); - mockRuntime.storage.projects.ssh.generateSSHKey.restore(); - mockRuntime.storage.projects.ssh.deleteSSHKey.restore(); - }) - - it('GET /settings/user/keys --- return empty list', function(done) { - mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve([])); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - res.body.keys.should.be.empty(); - done(); - }); - }); - - it('GET /settings/user/keys --- return normal list', function(done) { - var fileList = [ - 'test_key01', - 'test_key02' - ]; - var retList = fileList.map(function(elem) { - return { - name: elem - }; - }); - mockRuntime.storage.projects.ssh.listSSHKeys.returns(Promise.resolve(retList)); - request(app) - .get("/settings/user/keys") - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('keys'); - for (var item of retList) { - res.body.keys.should.containEql(item); - } - done(); - }); - }); - - it('GET /settings/user/keys --- return Error', function(done) { - var errInstance = new Error("Messages here....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.listSSHKeys.returns(p); - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return 404', function(done) { - mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(null)); - request(app) - .get("/settings/user/keys/NOT_REAL") - .expect(404) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - it('GET /settings/user/keys --- return Unexpected Error', function(done) { - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.listSSHKeys.returns(p); - request(app) - .get("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return content', function(done) { - var key_file_name = "test_key"; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - mockRuntime.storage.projects.ssh.getSSHKey.returns(Promise.resolve(fileContent)); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - mockRuntime.storage.projects.ssh.getSSHKey.called.should.be.true(); - res.body.should.be.deepEqual({ publickey: fileContent }); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.getSSHKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal(errInstance.code); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('GET /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.getSSHKey.returns(p); - request(app) - .get("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('POST /settings/user/keys --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name)); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(200) - .end(function(err,res) { - if (err) { - return done(err); - } - done(); - }); - }); - - it('POST /settings/user/keys --- return parameter error', function(done) { - var key_file_name = "test_key"; - mockRuntime.storage.projects.ssh.generateSSHKey.returns(Promise.resolve(key_file_name)); - request(app) - .post("/settings/user/keys") - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal("You need to have body or body.name"); - done(); - }); - }); - - it('POST /settings/user/keys --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.generateSSHKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('POST /settings/user/keys --- return Unexpected error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.generateSSHKey.returns(p); - request(app) - .post("/settings/user/keys") - .send({ name: key_file_name }) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- success', function(done) { - var key_file_name = "test_key"; - mockRuntime.storage.projects.ssh.deleteSSHKey.returns(Promise.resolve(true)); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(204) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.be.deepEqual({}); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - errInstance.code = "test_code"; - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("test_code"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.message); - done(); - }); - }); - - it('DELETE /settings/user/keys/ --- return Unexpected Error', function(done) { - var key_file_name = "test_key"; - var errInstance = new Error("Messages....."); - var p = Promise.reject(errInstance); - p.catch(()=>{}); - mockRuntime.storage.projects.ssh.deleteSSHKey.returns(p); - request(app) - .delete("/settings/user/keys/" + key_file_name) - .expect(400) - .end(function(err,res) { - if (err) { - return done(err); - } - res.body.should.have.property('error'); - res.body.error.should.be.equal("unexpected_error"); - res.body.should.have.property('message'); - res.body.message.should.be.equal(errInstance.toString()); - done(); - }); - }); -}); -*/ diff --git a/test/unit/@node-red/runtime/lib/flows/Flow_spec.js b/test/unit/@node-red/runtime/lib/flows/Flow_spec.js deleted file mode 100644 index eed4810c4..000000000 --- a/test/unit/@node-red/runtime/lib/flows/Flow_spec.js +++ /dev/null @@ -1,1327 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require('sinon'); -var clone = require('clone'); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var flowUtils = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util"); -var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); - - -describe('Flow', function() { - var getType; - - var stoppedNodes = {}; - var stoppedOrder = []; - var currentNodes = {}; - var rewiredNodes = {}; - var createCount = 0; - - beforeEach(function() { - currentNodes = {}; - stoppedNodes = {}; - stoppedOrder =[]; - rewiredNodes = {}; - createCount = 0; - Flow.init({settings:{},log:{ - log: sinon.stub(), // function() { console.log("l",[...arguments].map(a => JSON.stringify(a)).join(" ")) },// - debug: sinon.stub(), // function() { console.log("d",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - trace: sinon.stub(), // function() { console.log("t",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - warn: sinon.stub(), // function() { console.log("w",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - info: sinon.stub(), // function() { console.log("i",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - }}); - }); - - var TestNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - // console.log(this.id,msg.payload); - node.handled++; - node.send(msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestNode,Node); - - var TestErrorNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.name = n.name; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.error("test error",msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestErrorNode,Node); - - var TestAsyncNode = function(n) { - Node.call(this,n); - var node = this; - this.scope = n.scope; - this.uncaught = n.uncaught; - this.foo = n.foo; - this.handled = 0; - this.messages = []; - this.stopped = false; - this.closeDelay = n.closeDelay || 50; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - msg.handled = node.handled; - node.messages.push(msg); - node.send(msg); - }); - this.on('close',function(done) { - setTimeout(function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - done(); - },node.closeDelay); - }); - } - util.inherits(TestAsyncNode,Node); - - var TestDoneNode = function(n) { - Node.call(this,n); - var node = this; - this.scope = n.scope; - this.uncaught = n.uncaught; - this.foo = n.foo; - this.handled = 0; - this.messages = []; - this.stopped = false; - this.closeDelay = n.closeDelay || 50; - currentNodes[node.id] = node; - this.on('input',function(msg, send, done) { - node.handled++; - node.messages.push(msg); - send(msg); - done(); - }); - this.on('close',function(done) { - setTimeout(function() { - node.stopped = true; - stoppedNodes[node.id] = node; - stoppedOrder.push(node.id) - delete currentNodes[node.id]; - done(); - },node.closeDelay); - }); - } - util.inherits(TestDoneNode,Node); - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - if (type=="test") { - return TestNode; - } else if (type=="testError"){ - return TestErrorNode; - } else if (type=="testDone"){ - return TestDoneNode; - } else { - return TestAsyncNode; - } - }); - }); - after(function() { - getType.restore(); - }); - - describe('#constructor',function() { - it('called with an empty flow',function() { - var config = flowUtils.parseConfig([]); - var flow = Flow.create({},config); - - var nodeCount = 0; - Object.keys(flow.getActiveNodes()).length.should.equal(0); - }); - }); - - describe('#start',function() { - it("instantiates an initial configuration and stops it",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - flow.getNode('2').should.have.a.property('id','2'); - flow.getNode('3').should.have.a.property('id','3'); - flow.getNode('4').should.have.a.property('id','4'); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["2"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - - currentNodes["3"].on("input", function() { - currentNodes["1"].should.have.a.property("handled",1); - currentNodes["2"].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - - flow.stop().then(function() { - try { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - currentNodes.should.not.have.a.property("4"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - stoppedNodes.should.have.a.property("4"); - done(); - } catch(err) { - done(err); - } - }); - }); - currentNodes["1"].receive({payload:"test"}); - }); - - it("instantiates config nodes in the right order",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"5"}, // This node depends on #5 - {id:"5",z:"t1",type:"test"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(5); - - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - currentNodes.should.have.a.property("5"); - - currentNodes["1"].should.have.a.property("_index",2); - currentNodes["2"].should.have.a.property("_index",3); - currentNodes["3"].should.have.a.property("_index",4); - currentNodes["4"].should.have.a.property("_index",1); - currentNodes["5"].should.have.a.property("_index",0); - - flow.stop().then(function() { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - currentNodes.should.not.have.a.property("4"); - currentNodes.should.not.have.a.property("5"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - stoppedNodes.should.have.a.property("4"); - stoppedNodes.should.have.a.property("5"); - done(); - }); - }); - - - it("detects dependency loops in config nodes",function() { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5 - {id:"node2",z:"t1",type:"test",foo:"node1"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - /*jshint immed: false */ - (function(){ - flow.start(); - }).should.throw("Circular config node dependency detected: node1"); - }); - - it("rewires nodes specified by diff",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} - ]); - - var flow = Flow.create({},config,config.flows["t1"]); - createCount.should.equal(0); - flow.start(); - //TODO: use update to pass in new wiring and verify the change - createCount.should.equal(3); - flow.start({rewired:["2"]}); - createCount.should.equal(3); - rewiredNodes.should.have.a.property("2"); - done(); - }); - - it("instantiates a node with environment variable property values",function(done) { - after(function() { - delete process.env.NODE_RED_TEST_VALUE; - }) - process.env.NODE_RED_TEST_VALUE = "a-value"; - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE)",wires:[]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:{a:"$(NODE_RED_TEST_VALUE)"},wires:[]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:" $(NODE_RED_TEST_VALUE)",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE) ",wires:[]}, - {id:"5",x:10,y:10,z:"t1",type:"test",foo:"$(NODE_RED_TEST_VALUE_NONE)",wires:[]}, - {id:"6",x:10,y:10,z:"t1",type:"test",foo:["$(NODE_RED_TEST_VALUE)"],wires:[]} - ]); - var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].foo.should.equal("a-value"); - activeNodes["2"].foo.a.should.equal("a-value"); - activeNodes["3"].foo.should.equal(" $(NODE_RED_TEST_VALUE)"); - activeNodes["4"].foo.should.equal("$(NODE_RED_TEST_VALUE) "); - activeNodes["5"].foo.should.equal("$(NODE_RED_TEST_VALUE_NONE)"); - activeNodes["6"].foo[0].should.equal("a-value"); - - flow.stop().then(function() { - done(); - }); - }); - - it("ignores disabled nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",d:true,type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"}, - {id:"5",z:"t1",type:"test",d:true,foo:"a"} - - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(3); - - flow.getNode('1').should.have.a.property('id','1'); - should.not.exist(flow.getNode('2')); - flow.getNode('3').should.have.a.property('id','3'); - flow.getNode('4').should.have.a.property('id','4'); - should.not.exist(flow.getNode('5')); - - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - // Message doesn't reach 3 as 2 is disabled - currentNodes["3"].should.have.a.property("handled",0); - - flow.stop().then(function() { - try { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - currentNodes.should.not.have.a.property("4"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.not.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - stoppedNodes.should.have.a.property("4"); - done(); - } catch(err) { - done(err); - } - }); - },50); - }); - - }); - - describe('#stop', function() { - - - it("stops all nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"asyncTest",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - - flow.stop().then(function() { - currentNodes.should.not.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - stoppedNodes.should.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - done(); - }).catch(done); - }); - - it("stops specified nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - - flow.stop(["2"]).then(function() { - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.have.a.property("3"); - stoppedNodes.should.not.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.not.have.a.property("3"); - done(); - }); - }); - - it("stops config nodes last",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"c1",z:"t1",type:"test"}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"c2",z:"t1",type:"test"}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"c3",z:"t1",type:"test"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("c1"); - currentNodes.should.have.a.property("c2"); - currentNodes.should.have.a.property("c3"); - stoppedOrder.should.have.a.length(0); - - flow.stop().then(function() { - stoppedOrder.should.eql([ '1', '2', '3', 'c1', 'c2', 'c3' ]); - done(); - }).catch(done); - }); - - - it("Times out a node that fails to close", function(done) { - Flow.init({settings:{nodeCloseTimeout:50},log:{ - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - }}); - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"testAsync",closeDelay: 80, foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - currentNodes.should.have.a.property("1"); - currentNodes.should.have.a.property("2"); - currentNodes.should.have.a.property("3"); - - flow.stop().then(function() { - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.not.have.a.property("3"); - stoppedNodes.should.not.have.a.property("1"); - stoppedNodes.should.have.a.property("2"); - stoppedNodes.should.have.a.property("3"); - setTimeout(function() { - currentNodes.should.not.have.a.property("1"); - stoppedNodes.should.have.a.property("1"); - done(); - },40) - }); - }); - - }); - - describe('#getNode',function() { - it("gets a node known to the flow",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - - flow.stop().then(() => { done() }); - }); - - it("passes to parent if node not known locally",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({ - getNode: id => { return {id:id}} - },config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - - flow.getNode('parentNode').should.have.a.property('id','parentNode'); - - - flow.stop().then(() => { done() }); - }); - - it("does not pass to parent if cancelBubble set",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",z:"t1",type:"test",foo:"a"} - ]); - var flow = Flow.create({ - getNode: id => { return {id:id}} - },config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(4); - - flow.getNode('1').should.have.a.property('id','1'); - - should.not.exist(flow.getNode('parentNode',true)); - flow.stop().then(() => { done() }); - }); - }); - - describe("#handleStatus",function() { - it("passes a status event to the adjacent status node",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(5); - - - flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status",random:"otherProperty"}); - - setTimeout(function() { - - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","my-status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","1"); - statusMessage.status.source.should.have.a.property("type","test"); - statusMessage.status.source.should.have.a.property("name","a"); - - currentNodes["sn2"].should.have.a.property("handled",1); - statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","my-status"); - statusMessage.status.should.have.a.property("random","otherProperty"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","1"); - statusMessage.status.source.should.have.a.property("type","test"); - statusMessage.status.source.should.have.a.property("name","a"); - - - flow.stop().then(function() { - done(); - }); - },50) - }); - it("passes a status event to the adjacent scoped status node ",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",scope:["2"],foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"status",scope:["1"],foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(5); - - - flow.handleStatus(config.flows["t1"].nodes["1"],{text:"my-status"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",0); - currentNodes["sn2"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","my-status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","1"); - statusMessage.status.source.should.have.a.property("type","test"); - statusMessage.status.source.should.have.a.property("name","a"); - - - flow.stop().then(function() { - done(); - }); - },50); - }); - - }); - - describe("#handleError",function() { - it("passes an error event to the adjacent catch node",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]}, - {id:"sn3",x:10,y:10,z:"t1",type:"catch",uncaught:true,wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(6); - - - flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - currentNodes["sn2"].should.have.a.property("handled",1); - statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - // Node sn3 has uncaught:true - so should not get called - currentNodes["sn3"].should.have.a.property("handled",0); - - - flow.stop().then(function() { - done(); - }); - },50); - }); - it("passes an error event to the adjacent scoped catch node ",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",scope:["2"],foo:"a",wires:[]}, - {id:"sn2",x:10,y:10,z:"t1",type:"catch",scope:["1"],foo:"a",wires:[]}, - {id:"sn3",x:10,y:10,z:"t1",type:"catch",uncaught:true,wires:[]}, - {id:"sn4",x:10,y:10,z:"t1",type:"catch",uncaught:true,wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(7); - - flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",0); - currentNodes["sn2"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn2"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - // Node sn3/4 have uncaught:true - so should not get called - currentNodes["sn3"].should.have.a.property("handled",0); - currentNodes["sn4"].should.have.a.property("handled",0); - - // Inject error that sn1/2 will ignore - so should get picked up by sn3 - flow.handleError(config.flows["t1"].nodes["3"],"my-error-2",{a:"foo-2"}); - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",0); - currentNodes["sn2"].should.have.a.property("handled",1); - currentNodes["sn3"].should.have.a.property("handled",1); - currentNodes["sn4"].should.have.a.property("handled",1); - - statusMessage = currentNodes["sn3"].messages[0]; - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error-2"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","3"); - statusMessage.error.source.should.have.a.property("type","test"); - - flow.stop().then(function() { - done(); - }); - },50); - },50); - }); - it("moves any existing error object sideways",function(done){ - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - flow.handleError(config.flows["t1"].nodes["1"],"my-error",{a:"foo",error:"existing"}); - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("_error","existing"); - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","my-error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("id","1"); - statusMessage.error.source.should.have.a.property("type","test"); - statusMessage.error.source.should.have.a.property("name","a"); - - flow.stop().then(function() { - done(); - }); - },50); - }); - it("prevents an error looping more than 10 times",function(){}); - }); - - describe("#handleComplete",function() { - it("passes a complete event to the adjacent Complete node",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"testDone",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"testDone",foo:"a",wires:[]}, - {id:"cn",x:10,y:10,z:"t1",type:"complete",scope:["1","3"],foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(4); - - var msg = {payload: "hello world"} - var n1 = currentNodes["1"].receive(msg); - setTimeout(function() { - currentNodes["cn"].should.have.a.property("handled",2); - currentNodes["cn"].messages[0].should.have.a.property("handled",1); - currentNodes["cn"].messages[1].should.have.a.property("handled",2); - flow.stop().then(function() { - done(); - }); - },50); - }); - }); - - - describe("#send", function() { - it("sends a message - no cloning", function(done) { - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - var messageReceived = false; - n2.receive = function(msg) { - messageReceived = true; - try { - msg.should.be.exactly(message); - shutdownTest(); - } catch(err) { - shutdownTest(err); - } - } - - var message = {payload:"hello"} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: false - }]) - messageReceived.should.be.false() - }) - it("sends a message - cloning", function(done) { - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - - n2.receive = function(msg) { - try { - // Message should be cloned - msg.should.be.eql(message); - msg.should.not.be.exactly(message); - shutdownTest(); - } catch(err) { - shutdownTest(err); - } - } - - var message = {payload:"hello"} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - }) - it("sends multiple messages", function(done) { - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - - var messageCount = 0; - n2.receive = function(msg) { - try { - msg.should.be.exactly(messages[messageCount++]); - if (messageCount === 2) { - shutdownTest(); - } - } catch(err) { - shutdownTest(err); - } - } - - var messages = [{payload:"hello"},{payload:"world"}]; - - flow.send([{ - msg: messages[0], - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined } - },{ - msg: messages[1], - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined } - }]) - }) - it("sends a message - triggers hooks", function(done) { - var hookErrors = []; - var messageReceived = false; - var hooksCalled = []; - hooks.add("onSend", function(sendEvents) { - hooksCalled.push("onSend") - try { - messageReceived.should.be.false() - sendEvents.should.have.length(1); - sendEvents[0].msg.should.be.exactly(message); - } catch(err) { - hookErrors.push(err); - } - }) - hooks.add("preRoute", function(sendEvent) { - hooksCalled.push("preRoute") - try { - messageReceived.should.be.false() - sendEvent.msg.should.be.exactly(message); - should.not.exist(sendEvent.destination.node) - } catch(err) { - hookErrors.push(err); - } - - }) - hooks.add("preDeliver", function(sendEvent) { - hooksCalled.push("preDeliver") - try { - messageReceived.should.be.false() - // Cloning should have happened - sendEvent.msg.should.not.be.exactly(message); - // Destinatino node populated - should.exist(sendEvent.destination.node) - } catch(err) { - hookErrors.push(err); - } - - }) - hooks.add("postDeliver", function(sendEvent) { - hooksCalled.push("postDeliver") - try { - messageReceived.should.be.false() - - } catch(err) { - hookErrors.push(err); - } - - }) - var shutdownTest = function(err) { - hooks.clear(); - flow.stop().then(() => { done(err) }); - } - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - - Object.keys(flow.getActiveNodes()).should.have.length(2); - - var n1 = flow.getNode('1'); - var n2 = flow.getNode('2'); - n2.receive = function(msg) { - messageReceived = true; - try { - msg.should.be.eql(message); - msg.should.not.be.exactly(message); - hooksCalled.should.eql(["onSend","preRoute","preDeliver","postDeliver"]) - if (hookErrors.length > 0) { - shutdownTest(hookErrors[0]) - } else { - shutdownTest(); - } - } catch(err) { - shutdownTest(err); - } - } - - var message = {payload:"hello"} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - }) - - describe("errors thrown by hooks are reported to the sending node", function() { - var flow; - var n1,n2; - var messageReceived = false; - var errorReceived = null; - before(function() { - hooks.add("onSend", function(sendEvents) { - if (sendEvents[0].msg.payload === "trigger-onSend") { - throw new Error("onSend Error"); - } - }) - hooks.add("preRoute", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preRoute") { - throw new Error("preRoute Error"); - } - }) - hooks.add("preDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preDeliver") { - throw new Error("preDeliver Error"); - } - }) - hooks.add("postDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-postDeliver") { - throw new Error("postDeliver Error"); - } - }) - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - n1 = flow.getNode('1'); - n2 = flow.getNode('2'); - n2.receive = function(msg) { - messageReceived = true; - } - n1.error = function(err) { - errorReceived = err; - } - - }) - after(function(done) { - hooks.clear(); - flow.stop().then(() => { done() }); - }) - beforeEach(function() { - messageReceived = false; - errorReceived = null; - }) - function testHook(hook, msgExpected, done) { - var message = {payload:"trigger-"+hook} - flow.send([{ - msg: message, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - setTimeout(function() { - try { - messageReceived.should.equal(msgExpected); - should.exist(errorReceived) - errorReceived.toString().should.containEql(hook); - done(); - } catch(err) { - done(err); - } - },10) - } - - it("onSend", function(done) { testHook("onSend", false, done) }) - it("preRoute", function(done) { testHook("preRoute", false, done) }) - it("preDeliver", function(done) { testHook("preDeliver", false, done) }) - it("postDeliver", function(done) { testHook("postDeliver", true, done) }) - }) - - describe("hooks can stop the sending of messages", function() { - var flow; - var n1,n2; - var messageReceived = false; - var errorReceived = false; - before(function() { - hooks.add("onSend", function(sendEvents) { - if (sendEvents[0].msg.payload === "trigger-onSend") { - return false - } - }) - hooks.add("preRoute", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preRoute") { - return false - } - }) - hooks.add("preDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-preDeliver") { - return false - } - }) - hooks.add("postDeliver", function(sendEvent) { - if (sendEvent.msg.payload === "trigger-postDeliver") { - return false - } - }) - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]} - ]); - flow = Flow.create({},config,config.flows["t1"]); - flow.start(); - n1 = flow.getNode('1'); - n2 = flow.getNode('2'); - n2.receive = function(msg) { - messageReceived = true; - } - n1.error = function(err) { - errorReceived = true; - } - - }) - after(function(done) { - hooks.clear(); - flow.stop().then(() => { done() }); - }) - function testSend(payload,messageReceivedExpected,errorReceivedExpected,done) { - messageReceived = false; - errorReceived = false; - flow.send([{ - msg: {payload: payload}, - source: { id:"1", node: n1 }, - destination: { id:"2", node: undefined }, - cloneMessage: true - }]) - setTimeout(function() { - try { - messageReceived.should.eql(messageReceivedExpected) - errorReceived.should.eql(errorReceivedExpected) - done(); - } catch(err) { - done(err); - } - },10) - } - function testHook(hook, done) { - testSend("pass",true,false,err => { - if (err) { - done(err) - } else { - testSend("trigger-"+hook,false,false,done); - } - }) - } - - it("onSend", function(done) { testHook("onSend", done) }) - it("preRoute", function(done) { testHook("preRoute", done) }) - it("preDeliver", function(done) { testHook("preDeliver", done) }) - // postDeliver happens after delivery is scheduled so cannot stop it - // it("postDeliver", function(done) { testHook("postDeliver", done) }) - }) - }) - - describe("#env", function () { - it("can instantiate a node with environment variable property values of group and tab", function (done) { - try { - after(function() { - delete process.env.V0; - delete process.env.V1; - }) - process.env.V0 = "gv0"; - process.env.V1 = "gv1"; - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab",env:[ - {"name": "V0", value: "v0", type: "str"} - ]}, - {id:"g1",type:"group",z:"t1",env:[ - {"name": "V0", value: "v1", type: "str"}, - {"name": "V1", value: "v2", type: "str"} - ]}, - {id:"g2",type:"group",z:"t1",g:"g1",env:[ - {"name": "V1", value: "v3", type: "str"} - ]}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"$(V0)",wires:[]}, - {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V0)",wires:[]}, - {id:"3",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"$(V1)",wires:[]}, - {id:"4",x:10,y:10,z:"t1",g:"g2",type:"test",foo:"$(V1)",wires:[]}, - {id:"5",x:10,y:10,z:"t1",type:"test",foo:"$(V1)",wires:[]}, - ]); - var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].foo.should.equal("v0"); - activeNodes["2"].foo.should.equal("v1"); - activeNodes["3"].foo.should.equal("v2"); - activeNodes["4"].foo.should.equal("v3"); - activeNodes["5"].foo.should.equal("gv1"); - - flow.stop().then(function() { - done(); - }); - } - catch (e) { - console.log(e.stack); - done(e); - } - - }); - it("can access environment variable property using $parent", function (done) { - try { - after(function() { - delete process.env.V0; - delete process.env.V1; - }) - process.env.V0 = "gv0"; - process.env.V1 = "gv1"; - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab",env:[ - {"name": "V0", value: "v0", type: "str"} - ]}, - {id:"g1",type:"group",z:"t1",env:[ - {"name": "V0", value: "v1", type: "str"}, - {"name": "V1", value: "v2", type: "str"} - ]}, - {id:"g2",type:"group",z:"t1",g:"g1",env:[ - {"name": "V1", value: "v3", type: "str"} - ]}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V0}",wires:[]}, - {id:"2",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"${$parent.V0}",wires:[]}, - {id:"3",x:10,y:10,z:"t1",g:"g1",type:"test",foo:"${$parent.V1}",wires:[]}, - {id:"4",x:10,y:10,z:"t1",g:"g2",type:"test",foo:"${$parent.V1}",wires:[]}, - {id:"5",x:10,y:10,z:"t1",type:"test",foo:"${$parent.V1}",wires:[]}, - ]); - var flow = Flow.create({getSetting:v=>process.env[v]},config,config.flows["t1"]); - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].foo.should.equal("gv0"); - activeNodes["2"].foo.should.equal("v0"); - activeNodes["3"].foo.should.equal("gv1"); - activeNodes["4"].foo.should.equal("v2"); - activeNodes["5"].foo.should.equal("gv1"); - - flow.stop().then(function() { - done(); - }); - } - catch (e) { - console.log(e.stack); - done(e); - } - - }); - - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/flows/Subflow_spec.js b/test/unit/@node-red/runtime/lib/flows/Subflow_spec.js deleted file mode 100644 index 0479b5117..000000000 --- a/test/unit/@node-red/runtime/lib/flows/Subflow_spec.js +++ /dev/null @@ -1,885 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var sinon = require('sinon'); -var clone = require('clone'); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var Subflow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Subflow"); -var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); - -var flowUtils = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); - -describe('Subflow', function() { - var getType; - - var stoppedNodes = {}; - var currentNodes = {}; - var rewiredNodes = {}; - var createCount = 0; - - beforeEach(function() { - currentNodes = {}; - stoppedNodes = {}; - rewiredNodes = {}; - createCount = 0; - var runtime = { - settings:{}, - log:{ - log: sinon.stub(), // function() { console.log("l",[...arguments].map(a => JSON.stringify(a)).join(" ")) },// - debug: sinon.stub(), // function() { console.log("d",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - trace: sinon.stub(), // function() { console.log("t",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - warn: sinon.stub(), // function() { console.log("w",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - info: sinon.stub(), // function() { console.log("i",[...arguments].map(a => JSON.stringify(a)).join(" ")) },//sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - } - } - Flow.init(runtime); - Subflow.init(runtime); - }); - - var TestNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - this.received = null; - currentNodes[node.id] = node; - this.on('input',function(msg) { - // console.log(this.id,msg.payload); - node.handled++; - node.received = msg.payload; - node.send(msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestNode,Node); - - var TestErrorNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.name = n.name; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.error("test error",msg); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestErrorNode,Node); - - - var TestStatusNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.name = n.name; - var node = this; - this.foo = n.foo; - this.handled = 0; - this.stopped = false; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.status({text:"test status"}); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestStatusNode,Node); - - var TestAsyncNode = function(n) { - Node.call(this,n); - var node = this; - this.scope = n.scope; - this.foo = n.foo; - this.handled = 0; - this.messages = []; - this.stopped = false; - this.closeDelay = n.closeDelay || 50; - currentNodes[node.id] = node; - this.on('input',function(msg) { - node.handled++; - node.messages.push(msg); - node.send(msg); - }); - this.on('close',function(done) { - setTimeout(function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - done(); - },node.closeDelay); - }); - } - util.inherits(TestAsyncNode,Node); - - var TestEnvNode = function(n) { - Node.call(this,n); - this._index = createCount++; - this.scope = n.scope; - this.foo = n.foo; - var node = this; - this.stopped = false; - this.received = null; - currentNodes[node.id] = node; - this.on('input',function(msg) { - var flow = node._flow; - var val = flow.getSetting("__KEY__"); - node.received = val; - node.send({payload: val}); - }); - this.on('close',function() { - node.stopped = true; - stoppedNodes[node.id] = node; - delete currentNodes[node.id]; - }); - this.__updateWires = this.updateWires; - this.updateWires = function(newWires) { - rewiredNodes[node.id] = node; - node.newWires = newWires; - node.__updateWires(newWires); - }; - } - util.inherits(TestEnvNode,Node); - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - if (type=="test") { - return TestNode; - } else if (type=="testError"){ - return TestErrorNode; - } else if (type=="testStatus"){ - return TestStatusNode; - } else if (type=="testEnv"){ - return TestEnvNode; - } else { - return TestAsyncNode; - } - }); - }); - after(function() { - getType.restore(); - }); - describe('#start',function() { - it("instantiates a subflow and stops it",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3","4"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]},{"wires":[{"id":"sf1","port":0}]}]}, - {id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]}, - {id:"sf1-2",type:"test","z":"sf1",foo:"sf1-cn",x:166,y:99,"wires":[[]]}, - {id:"sf1-cn",type:"test","z":"sf1"} - ]); - var flow = Flow.create({handleError: (a,b,c) => { console.log(a,b,c); }},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(4); - // var sfInstanceId = Object.keys(activeNodes)[5]; - // var sfInstanceId2 = Object.keys(activeNodes)[6]; - var sfConfigId = Object.keys(activeNodes)[4]; - - flow.getNode('1').should.have.a.property('id','1'); - flow.getNode('2').should.have.a.property('id','2'); - flow.getNode('3').should.have.a.property('id','3'); - flow.getNode('4').should.have.a.property('id','4'); - // flow.getNode(sfInstanceId).should.have.a.property('id',sfInstanceId); - // flow.getNode(sfInstanceId2).should.have.a.property('id',sfInstanceId2); - // flow.getNode(sfConfigId).should.have.a.property('id',sfConfigId); - - // flow.getNode(sfInstanceId2).should.have.a.property('foo',sfConfigId); - - Object.keys(currentNodes).should.have.length(6); - - currentNodes.should.have.a.property("1"); - currentNodes.should.not.have.a.property("2"); - currentNodes.should.have.a.property("3"); - currentNodes.should.have.a.property("4"); - // currentNodes.should.have.a.property(sfInstanceId); - // currentNodes.should.have.a.property(sfInstanceId2); - // currentNodes.should.have.a.property(sfConfigId); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - currentNodes["4"].should.have.a.property("handled",0); - // currentNodes[sfInstanceId].should.have.a.property("handled",0); - // currentNodes[sfInstanceId2].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - // currentNodes[sfInstanceId].should.have.a.property("handled",1); - // currentNodes[sfInstanceId2].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - currentNodes["4"].should.have.a.property("handled",1); - - - - flow.stop().then(function() { - Object.keys(currentNodes).should.have.length(0); - Object.keys(stoppedNodes).should.have.length(6); - - // currentNodes.should.not.have.a.property("1"); - // currentNodes.should.not.have.a.property("3"); - // currentNodes.should.not.have.a.property("4"); - // // currentNodes.should.not.have.a.property(sfInstanceId); - // // currentNodes.should.not.have.a.property(sfInstanceId2); - // // currentNodes.should.not.have.a.property(sfConfigId); - // stoppedNodes.should.have.a.property("1"); - // stoppedNodes.should.have.a.property("3"); - // stoppedNodes.should.have.a.property("4"); - // // stoppedNodes.should.have.a.property(sfInstanceId); - // // stoppedNodes.should.have.a.property(sfInstanceId2); - // // stoppedNodes.should.have.a.property(sfConfigId); - done(); - }); - },150); - }); - it("instantiates a subflow inside a subflow and stops it",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3","4"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 1","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]}]}, - {id:"sf2",type:"subflow","name":"Subflow 2","info":"", - "in":[{wires:[]}],"out":[{"wires":[{"id":"sf2","port":0}]}]}, - {id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]}, - {id:"sf1-2",type:"subflow:sf2","z":"sf1",x:166,y:99,"wires":[[]]} - - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - flow.stop().then(function() { - Object.keys(currentNodes).should.have.length(0); - done(); - }); - },150); - }); - it("rewires a subflow node on update/start",function(done){ - - var rawConfig = [ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"4",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-2","port":0}]}]}, - {id:"sf1-1",type:"test1","z":"sf1",x:166,y:99,"wires":[["sf1-2"]]}, - {id:"sf1-2",type:"test2","z":"sf1",x:166,y:99,"wires":[[]]} - ]; - - var config = flowUtils.parseConfig(clone(rawConfig)); - - rawConfig[2].wires = [["4"]]; - - var newConfig = flowUtils.parseConfig(rawConfig); - var diff = flowUtils.diffConfigs(config,newConfig); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(4); - // var sfInstanceId = Object.keys(activeNodes)[4]; - // var sfInstanceId2 = Object.keys(activeNodes)[5]; - - currentNodes["1"].should.have.a.property("handled",0); - currentNodes["3"].should.have.a.property("handled",0); - currentNodes["4"].should.have.a.property("handled",0); - - currentNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["1"].should.have.a.property("handled",1); - // currentNodes[sfInstanceId].should.have.a.property("handled",1); - // currentNodes[sfInstanceId2].should.have.a.property("handled",1); - currentNodes["3"].should.have.a.property("handled",1); - currentNodes["4"].should.have.a.property("handled",0); - - flow.update(newConfig,newConfig.flows["t1"]); - flow.start(diff) - - currentNodes["1"].receive({payload:"test2"}); - setTimeout(function() { - - currentNodes["1"].should.have.a.property("handled",2); - // currentNodes[sfInstanceId].should.have.a.property("handled",2); - // currentNodes[sfInstanceId2].should.have.a.property("handled",2); - currentNodes["3"].should.have.a.property("handled",1); - currentNodes["4"].should.have.a.property("handled",1); - - - flow.stop().then(function() { - done(); - }); - },150); - },150); - }); - }); - describe('#stop', function() { - it("stops subflow instance nodes",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",type:"test","z":"sf1",x:166,y:99,"wires":[[]]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - Object.keys(activeNodes).should.have.length(3); - Object.keys(stoppedNodes).should.have.length(0); - flow.stop(["2"]).then(function() { - Object.keys(currentNodes).should.have.length(2); - Object.keys(stoppedNodes).should.have.length(1); - done(); - }).catch(done); - }); - }); - describe("#handleStatus",function() { - it("passes a status event to the subflow's parent tab status node - all scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","test status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("type","testStatus"); - statusMessage.status.source.should.have.a.property("name","test-status-node"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - it("passes a status event to the subflow's parent tab status node - targetted scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",scope:["2"],wires:[]} - ]); - var parentFlowStatusCalled = false; - - var flow = Flow.create({handleStatus:() => { parentFlowStatusCalled = true} },config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - parentFlowStatusCalled.should.be.false(); - - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","test status"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("type","testStatus"); - statusMessage.status.source.should.have.a.property("name","test-status-node"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - }); - - describe("status node", function() { - it("emits a status event when a message is passed to a subflow-status node - msg.payload as string", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[{id:"sf1-1", port:0}]} - }, - {id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test-payload"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","test-payload"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","2"); - statusMessage.status.source.should.have.a.property("type","subflow:sf1"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - it("emits a status event when a message is passed to a subflow-status node - msg.payload as status obj", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[{id:"sf1-1", port:0}]} - }, - {id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:{text:"payload-obj"}}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","payload-obj"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","2"); - statusMessage.status.source.should.have.a.property("type","subflow:sf1"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - it("emits a status event when a message is passed to a subflow-status node - msg.status", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[{id:"sf1-1", port:0}]} - }, - {id:"sf1-1",type:"test",name:"test","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({status:{text:"status-obj"}}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("status"); - statusMessage.status.should.have.a.property("text","status-obj"); - statusMessage.status.should.have.a.property("source"); - statusMessage.status.source.should.have.a.property("id","2"); - statusMessage.status.source.should.have.a.property("type","subflow:sf1"); - - flow.stop().then(function() { - - done(); - }); - },150); - }); - it("does not emit a regular status event if it contains a subflow-status node", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - { - id:"sf1", - type:"subflow", - name:"Subflow 2", - info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-1",port:0}]}], - status:{wires:[]} - }, - {id:"sf1-1",type:"testStatus",name:"test-status-node","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"status",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test-payload"}); - - currentNodes["sn"].should.have.a.property("handled",0); - - flow.stop().then(function() { - - done(); - }); - }); - }) - - describe("#handleError",function() { - it("passes an error event to the subflow's parent tab catch node - all scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",name:"test-error-node",type:"testError","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",foo:"a",wires:[]} - ]); - var flow = Flow.create({},config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","test error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("type","testError"); - statusMessage.error.source.should.have.a.property("name","test-error-node"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - it("passes an error event to the subflow's parent tab catch node - targetted scope",function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",name:"a",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, - {id:"sf1",type:"subflow","name":"Subflow 2","info":"", - "in":[{"wires":[{"id":"sf1-1"}]}],"out":[{"wires":[{"id":"sf1-1","port":0}]}]}, - {id:"sf1-1",name:"test-error-node",type:"testError","z":"sf1",x:166,y:99,"wires":[[]]}, - {id:"sn",x:10,y:10,z:"t1",type:"catch",scope:["2"],wires:[]} - ]); - var parentFlowErrorCalled = false; - var flow = Flow.create({handleError:() => { parentFlowErrorCalled = true} },config,config.flows["t1"]); - - flow.start(); - - var activeNodes = flow.getActiveNodes(); - - activeNodes["1"].receive({payload:"test"}); - - setTimeout(function() { - parentFlowErrorCalled.should.be.false(); - - currentNodes["sn"].should.have.a.property("handled",1); - var statusMessage = currentNodes["sn"].messages[0]; - - statusMessage.should.have.a.property("error"); - statusMessage.error.should.have.a.property("message","test error"); - statusMessage.error.should.have.a.property("source"); - statusMessage.error.source.should.have.a.property("type","testError"); - statusMessage.error.source.should.have.a.property("name","test-error-node"); - - flow.stop().then(function() { - done(); - }); - },150); - - }); - }); - - describe("#env var", function() { - // should be changed according to internal env var representation - function setEnv(node, key, val) { - var flow = node._flow; - if (flow) { - var env = flow.env; - if (!env) { - env = flow.env = {}; - } - env[key] = { - name: key, - type: "str", - value: val - }; - } - } - - it("can access process env var", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, - {id:"sf1",type:"subflow",name:"Subflow 2",info:"", - "in":[ {wires:[{id:"sf1-1"}]} ], - "out":[ {wires:[{id:"sf1-2",port:0}]} ]}, - {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, - {id:"sf1-2",type:"testEnv",z:"sf1",foo:"sf1-cn",x:166,y:99,wires:[[]]} - ]); - var flow = Flow.create({ - getSetting: k=> process.env[k], - handleError: (a,b,c) => { console.log(a,b,c); } - },config,config.flows["t1"]); - - flow.start(); - - process.env["__KEY__"] = "__VAL__"; - - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL__"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - - it("can access subflow env var", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, - {id:"sf1",type:"subflow",name:"Subflow 2",info:"", - "in":[ {wires:[{id:"sf1-1"}]} ], - "out":[ {wires:[{id:"sf1-2",port:0}]} ]}, - {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, - {id:"sf1-2",type:"testEnv",z:"sf1",foo:"sf1.2",x:166,y:99,wires:[[]]} - ]); - var flow = Flow.create({ - getSetting: k=> process.env[k], - handleError: (a,b,c) => { console.log(a,b,c); } - },config,config.flows["t1"]); - - flow.start(); - - var testenv_node = null; - for (var n in currentNodes) { - var node = currentNodes[n]; - if (node.type === "testEnv") { - testenv_node = node; - break; - } - } - process.env["__KEY__"] = "__VAL0__"; - setEnv(testenv_node, "__KEY__", "__VAL1__"); - - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL1__"); - - flow.stop().then(function() { - done(); - }); - },150); - }); - - it("can access nested subflow env var", function(done) { - var config = flowUtils.parseConfig([ - {id:"t1",type:"tab"}, - {id:"1",x:10,y:10,z:"t1",type:"test",foo:"t1.1",wires:["2"]}, - {id:"2",x:10,y:10,z:"t1",type:"subflow:sf1",wires:["3"]}, - {id:"3",x:10,y:10,z:"t1",type:"test",foo:"t1.3",wires:[]}, - {id:"sf1",type:"subflow",name:"Subflow 1",info:"", - in:[{wires:[{id:"sf1-1"}]}], - out:[{wires:[{id:"sf1-2",port:0}]}]}, - {id:"sf2",type:"subflow",name:"Subflow 2",info:"", - in:[{wires:[{id:"sf2-1"}]}], - out:[{wires:[{id:"sf2-2",port:0}]}]}, - {id:"sf1-1",type:"test",z:"sf1",foo:"sf1.1",x:166,y:99,wires:[["sf1-2"]]}, - {id:"sf1-2",type:"subflow:sf2",z:"sf1",x:166,y:99,wires:[[]]}, - {id:"sf2-1",type:"test",z:"sf2",foo:"sf2.1",x:166,y:99,wires:[["sf2-2"]]}, - {id:"sf2-2",type:"testEnv",z:"sf2",foo:"sf2.2",x:166,y:99,wires:[[]]}, - ]); - var flow = Flow.create({ - getSetting: k=> process.env[k], - handleError: (a,b,c) => { console.log(a,b,c); } - },config,config.flows["t1"]); - - flow.start(); - - var node_sf1_1 = null; - var node_sf2_1 = null; - var testenv_node = null; - for (var n in currentNodes) { - var node = currentNodes[n]; - if (node.foo === "sf1.1") { - node_sf1_1 = node; - } - if (node.foo === "sf2.1") { - node_sf2_1 = node; - } - } - - process.env["__KEY__"] = "__VAL0__"; - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL0__"); - - setEnv(node_sf1_1, "__KEY__", "__VAL1__"); - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL1__"); - - setEnv(node_sf2_1, "__KEY__", "__VAL2__"); - currentNodes["1"].receive({payload: "test"}); - setTimeout(function() { - currentNodes["3"].should.have.a.property("received", "__VAL2__"); - - flow.stop().then(function() { - done(); - }); - },150); - },150); - },150); - }); - - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/flows/index_spec.js b/test/unit/@node-red/runtime/lib/flows/index_spec.js deleted file mode 100644 index 737846100..000000000 --- a/test/unit/@node-red/runtime/lib/flows/index_spec.js +++ /dev/null @@ -1,669 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var clone = require("clone"); -var NR_TEST_UTILS = require("nr-test-utils"); - -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var RedNode = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var RED = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes"); -var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); -var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry") -var Flow = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/Flow"); - -describe('flows/index', function() { - - var storage; - var eventsOn; - var credentialsClean; - var credentialsLoad; - var credentialsAdd; - - var flowCreate; - var getType; - var checkFlowDependencies; - - var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - _: function() { return "abc"} - } - - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - return type.indexOf('missing') === -1; - }); - checkFlowDependencies = sinon.stub(typeRegistry, "checkFlowDependencies").callsFake(async function(flow) { - if (flow[0].id === "node-with-missing-modules") { - throw new Error("Missing module"); - } - }); - }); - - after(function() { - getType.restore(); - checkFlowDependencies.restore(); - }); - - - beforeEach(function() { - eventsOn = sinon.spy(events,"on"); - credentialsClean = sinon.stub(credentials,"clean").callsFake(function(conf) { - conf.forEach(function(n) { - delete n.credentials; - }); - return Promise.resolve(); - }); - credentialsLoad = sinon.stub(credentials,"load").callsFake(function(creds) { - if (creds && creds.hasOwnProperty("$") && creds['$'] === "fail") { - return Promise.reject("creds error"); - } - return Promise.resolve(); - }); - credentialsAdd = sinon.stub(credentials,"add").callsFake(async function(id, conf){}) - flowCreate = sinon.stub(Flow,"create").callsFake(function(parent, global, flow) { - var id; - if (typeof flow === 'undefined') { - flow = global; - id = '_GLOBAL_'; - } else { - id = flow.id; - } - flowCreate.flows[id] = { - flow: flow, - global: global, - start: sinon.spy(), - update: sinon.spy(), - stop: sinon.spy(), - getActiveNodes: function() { - return flow.nodes||{}; - }, - handleError: sinon.spy(), - handleStatus: sinon.spy() - - } - return flowCreate.flows[id]; - }); - flowCreate.flows = {}; - - storage = { - saveFlows: function(conf) { - storage.conf = conf; - return Promise.resolve(); - } - } - }); - - afterEach(function(done) { - eventsOn.restore(); - credentialsClean.restore(); - credentialsLoad.restore(); - credentialsAdd.restore(); - flowCreate.restore(); - - flows.stopFlows().then(done); - - }); - // describe('#init',function() { - // it('registers the type-registered handler', function() { - // flows.init({},{}); - // eventsOn.calledOnce.should.be.true(); - // }); - // }); - - describe('#setFlows', function() { - it('sets the full flow', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - credentialsClean.called.should.be.true(); - storage.hasOwnProperty('conf').should.be.true(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - - }); - it('loads the full flow for type load', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var loadStorage = { - saveFlows: function(conf) { - loadStorage.conf = conf; - return Promise.resolve(456); - }, - getFlows: function() { - return Promise.resolve({flows:originalConfig,rev:123}) - } - } - flows.init({log:mockLog, settings:{},storage:loadStorage}); - flows.setFlows(originalConfig,"load").then(function() { - credentialsClean.called.should.be.false(); - // 'load' type does not trigger a save - loadStorage.hasOwnProperty('conf').should.be.false(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - - }); - - it('extracts credentials from the full flow', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[],credentials:{"a":1}}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - credentialsClean.called.should.be.true(); - storage.hasOwnProperty('conf').should.be.true(); - var cleanedFlows = flows.getFlows(); - storage.conf.flows.should.eql(cleanedFlows.flows); - cleanedFlows.flows.should.not.eql(originalConfig); - cleanedFlows.flows[0].credentials = {"a":1}; - cleanedFlows.flows.should.eql(originalConfig); - done(); - }); - }); - - it('sets the full flow including credentials', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var credentials = {"t1-1":{"a":1}}; - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig,credentials).then(function() { - credentialsClean.called.should.be.true(); - credentialsAdd.called.should.be.true(); - credentialsAdd.lastCall.args[0].should.eql("t1-1"); - credentialsAdd.lastCall.args[1].should.eql({"a":1}); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - }); - - it('updates existing flows with partial deployment - nodes', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var newConfig = clone(originalConfig); - newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); - newConfig.push({id:"t2",type:"tab"}); - newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - events.once('flows:started',function() { - flows.setFlows(newConfig,"nodes").then(function() { - flows.getFlows().flows.should.eql(newConfig); - flowCreate.flows['t1'].update.called.should.be.true(); - flowCreate.flows['t2'].start.called.should.be.true(); - flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); - done(); - }) - }); - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - flows.startFlows(); - }); - }); - - it('updates existing flows with partial deployment - flows', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var newConfig = clone(originalConfig); - newConfig.push({id:"t1-2",x:10,y:10,z:"t1",type:"test",wires:[]}); - newConfig.push({id:"t2",type:"tab"}); - newConfig.push({id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}); - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - events.once('flows:started',function() { - flows.setFlows(newConfig,"nodes").then(function() { - flows.getFlows().flows.should.eql(newConfig); - flowCreate.flows['t1'].update.called.should.be.true(); - flowCreate.flows['t2'].start.called.should.be.true(); - flowCreate.flows['_GLOBAL_'].update.called.should.be.true(); - flows.stopFlows().then(done); - }) - }); - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - flows.startFlows(); - }); - }); - - it('returns error if it cannot decrypt credentials', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var credentials = {"$":"fail"}; - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig,credentials).then(function() { - done("Unexpected success when credentials couldn't be decrypted") - }).catch(function(err) { - done(); - }); - }); - }); - - describe('#load', function() { - it('loads the flow config', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - credentialsLoad.called.should.be.true(); - // 'load' type does not trigger a save - storage.hasOwnProperty('conf').should.be.false(); - flows.getFlows().flows.should.eql(originalConfig); - done(); - }); - }); - }); - - describe('#startFlows', function() { - it('starts the loaded config', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - events.once('flows:started',function() { - Object.keys(flowCreate.flows).should.eql(['_GLOBAL_','t1']); - done(); - }); - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }); - }); - it('does not start if nodes missing', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(() => { - try { - flowCreate.called.should.be.false(); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('starts when missing nodes registered', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - {id:"t1-3",x:10,y:10,z:"t1",type:"missing2",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(() => { - flowCreate.called.should.be.false(); - events.emit("type-registered","missing"); - setTimeout(function() { - flowCreate.called.should.be.false(); - events.emit("type-registered","missing2"); - setTimeout(function() { - flowCreate.called.should.be.true(); - done(); - },10); - },10); - }); - }); - - it('does not start if external modules missing', function(done) { - var originalConfig = [ - {id:"node-with-missing-modules",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - var receivedEvent = null; - var handleEvent = function(payload) { - receivedEvent = payload; - } - - events.on("runtime-event",handleEvent); - - //{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});" - - - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(flows.startFlows).then(() => { - events.removeListener("runtime-event",handleEvent); - try { - flowCreate.called.should.be.false(); - receivedEvent.should.have.property('id','runtime-state'); - receivedEvent.should.have.property('payload', - { error: 'missing-modules', - type: 'warning', - text: 'notification.warnings.missing-modules', - modules: [] } - ); - - done(); - }catch(err) { - done(err) - } - }); - }); - - }); - - describe.skip('#get',function() { - - }); - - describe('#eachNode', function() { - it('iterates the flow nodes', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - var c = 0; - flows.eachNode(function(node) { - c++ - }) - c.should.equal(2); - done(); - }); - }); - }); - - describe('#stopFlows', function() { - - }); - // describe('#handleError', function() { - // it('passes error to correct flow', function(done) { - // var originalConfig = [ - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - // {id:"t1",type:"tab"} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleError(originalConfig[0],"message",{}); - // flowCreate.flows['t1'].handleError.called.should.be.true(); - // done(); - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // it('passes error to flows that use the originating global config', function(done) { - // var originalConfig = [ - // {id:"configNode",type:"test"}, - // {id:"t1",type:"tab"}, - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, - // {id:"t2",type:"tab"}, - // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, - // {id:"t3",type:"tab"}, - // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleError(originalConfig[0],"message",{}); - // try { - // flowCreate.flows['t1'].handleError.called.should.be.true(); - // flowCreate.flows['t2'].handleError.called.should.be.false(); - // flowCreate.flows['t3'].handleError.called.should.be.true(); - // done(); - // } catch(err) { - // done(err); - // } - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // }); - // describe('#handleStatus', function() { - // it('passes status to correct flow', function(done) { - // var originalConfig = [ - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - // {id:"t1",type:"tab"} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleStatus(originalConfig[0],"message"); - // flowCreate.flows['t1'].handleStatus.called.should.be.true(); - // done(); - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // - // it('passes status to flows that use the originating global config', function(done) { - // var originalConfig = [ - // {id:"configNode",type:"test"}, - // {id:"t1",type:"tab"}, - // {id:"t1-1",x:10,y:10,z:"t1",type:"test",config:"configNode",wires:[]}, - // {id:"t2",type:"tab"}, - // {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]}, - // {id:"t3",type:"tab"}, - // {id:"t3-1",x:10,y:10,z:"t3",type:"test",config:"configNode",wires:[]} - // ]; - // storage.getFlows = function() { - // return Promise.resolve({flows:originalConfig}); - // } - // - // events.once('flows:started',function() { - // flows.handleStatus(originalConfig[0],"message"); - // try { - // flowCreate.flows['t1'].handleStatus.called.should.be.true(); - // flowCreate.flows['t2'].handleStatus.called.should.be.false(); - // flowCreate.flows['t3'].handleStatus.called.should.be.true(); - // done(); - // } catch(err) { - // done(err); - // } - // }); - // - // flows.init({log:mockLog, settings:{},storage:storage}); - // flows.load().then(function() { - // flows.startFlows(); - // }); - // }); - // }); - - describe('#checkTypeInUse', function() { - - before(function() { - sinon.stub(typeRegistry,"getNodeInfo").callsFake(function(id) { - if (id === 'unused-module') { - return {types:['one','two','three']} - } else { - return {types:['one','test','three']} - } - }); - }); - - after(function() { - typeRegistry.getNodeInfo.restore(); - }); - - it('returns cleanly if type not is use', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - flows.checkTypeInUse("unused-module"); - done(); - }); - }); - it('throws error if type is in use', function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - flows.init({log:mockLog, settings:{},storage:storage}); - flows.setFlows(originalConfig).then(function() { - /*jshint immed: false */ - try { - flows.checkTypeInUse("used-module"); - done("type_in_use error not thrown"); - } catch(err) { - err.code.should.eql("type_in_use"); - done(); - } - }); - }); - }); - - describe('#addFlow', function() { - it("rejects duplicate node id",function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - flows.addFlow({ - label:'new flow', - nodes:[ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]} - ] - }).then(function() { - done(new Error('failed to reject duplicate node id')); - }).catch(function(err) { - done(); - }) - }); - - }); - - it("addFlow",function(done) { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - storage.getFlows = function() { - return Promise.resolve({flows:originalConfig}); - } - storage.setFlows = function() { - return Promise.resolve(); - } - flows.init({log:mockLog, settings:{},storage:storage}); - flows.load().then(function() { - return flows.startFlows(); - }).then(function() { - flows.addFlow({ - label:'new flow', - nodes:[ - {id:"t2-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2-2",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2-3",z:"t1",type:"test"} - ] - }).then(function(id) { - flows.getFlows().flows.should.have.lengthOf(6); - var createdFlows = Object.keys(flowCreate.flows); - createdFlows.should.have.lengthOf(3); - createdFlows[2].should.eql(id); - done(); - }).catch(function(err) { - done(err); - }) - }); - - }); - }) - describe('#updateFlow', function() { - it.skip("updateFlow"); - }) - describe('#removeFlow', function() { - it.skip("removeFlow"); - }) - describe('#disableFlow', function() { - it.skip("disableFlow"); - }) - describe('#enableFlow', function() { - it.skip("enableFlow"); - }) -}); diff --git a/test/unit/@node-red/runtime/lib/flows/util_spec.js b/test/unit/@node-red/runtime/lib/flows/util_spec.js deleted file mode 100644 index 6a4571e87..000000000 --- a/test/unit/@node-red/runtime/lib/flows/util_spec.js +++ /dev/null @@ -1,801 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var clone = require("clone"); -var NR_TEST_UTILS = require("nr-test-utils"); -var flowUtil = NR_TEST_UTILS.require("@node-red/runtime/lib/flows/util"); -var typeRegistry = NR_TEST_UTILS.require("@node-red/registry"); -var redUtil = NR_TEST_UTILS.require("@node-red/util").util; - -describe('flows/util', function() { - var getType; - - before(function() { - getType = sinon.stub(typeRegistry,"get").callsFake(function(type) { - return type!=='missing'; - }); - }); - after(function() { - getType.restore(); - }); - - describe('#mapEnvVarProperties',function() { - before(function() { - process.env.foo1 = "bar1"; - process.env.foo2 = "bar2"; - process.env.foo3 = "bar3"; - }) - after(function() { - delete process.env.foo1; - delete process.env.foo2; - delete process.env.foo3; - }) - it('handles ENV substitutions in an object - $()', function() { - var foo = {a:"$(foo1)",b:"$(foo2)",c:{d:"$(foo3)"}}; - for (var p in foo) { - if (foo.hasOwnProperty(p)) { - flowUtil.mapEnvVarProperties(foo,p,{getSetting: p => process.env[p]}); - } - } - foo.should.eql({ a: 'bar1', b: 'bar2', c: { d: 'bar3' } } ); - }); - it('handles ENV substitutions in an object - ${}', function() { - var foo = {a:"${foo1}",b:"${foo2}",c:{d:"${foo3}"}}; - for (var p in foo) { - if (foo.hasOwnProperty(p)) { - flowUtil.mapEnvVarProperties(foo,p,{getSetting: p => process.env[p]}); - } - } - foo.should.eql({ a: 'bar1', b: 'bar2', c: { d: 'bar3' } } ); - }); - - it('gets ENV from parent flow', function() { - var foo = {a:"$(unknown)",b:"$(foo2)",c:{d:"$(foo3)"}}; - for (var p in foo) { - if (foo.hasOwnProperty(p)) { - flowUtil.mapEnvVarProperties(foo,p,{ - getSetting: name => name[0]==='f'?name.toUpperCase():undefined - }); - } - } - foo.should.eql({ a: '$(unknown)', b: 'FOO2', c: { d: 'FOO3' } } ); - }); - }); - describe('#getEnvVar',function() { - before(function() { - process.env.foo1 = "bar1"; - }) - after(function() { - delete process.env.foo1; - }) - it('returns a known env var', function() { - flowUtil.init({settings:{}}); - flowUtil.getEnvVar("foo1").should.equal("bar1") - }) - it('returns undefined for an unknown env var', function() { - flowUtil.init({settings:{}}); - (flowUtil.getEnvVar("foo2") === undefined).should.be.true() - }) - it('returns undefined for an excluded env var', function() { - flowUtil.init({settings:{envVarExcludes:['foo1']}}); - (flowUtil.getEnvVar("foo1") === undefined).should.be.true() - }) - - }); - - describe('#diffNodes',function() { - it('handles a null old node', function() { - flowUtil.diffNodes(null,{}).should.be.true(); - }); - it('ignores x/y changes', function() { - flowUtil.diffNodes({x:10,y:10},{x:20,y:10}).should.be.false(); - flowUtil.diffNodes({x:10,y:10},{x:10,y:20}).should.be.false(); - }); - it('ignores wiring changes', function() { - flowUtil.diffNodes({wires:[]},{wires:[1,2,3]}).should.be.false(); - }); - it('spots existing property change - string', function() { - flowUtil.diffNodes({a:"foo"},{a:"bar"}).should.be.true(); - }); - it('spots existing property change - number', function() { - flowUtil.diffNodes({a:0},{a:1}).should.be.true(); - }); - it('spots existing property change - boolean', function() { - flowUtil.diffNodes({a:true},{a:false}).should.be.true(); - }); - it('spots existing property change - truthy', function() { - flowUtil.diffNodes({a:true},{a:1}).should.be.true(); - }); - it('spots existing property change - falsey', function() { - flowUtil.diffNodes({a:false},{a:0}).should.be.true(); - }); - it('spots existing property change - array', function() { - flowUtil.diffNodes({a:[0,1,2]},{a:[0,2,3]}).should.be.true(); - }); - it('spots existing property change - object', function() { - flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{a:[0,2,3]}}).should.be.true(); - flowUtil.diffNodes({a:{a:[0,1,2]}},{a:{b:[0,1,2]}}).should.be.true(); - }); - it('spots added property', function() { - flowUtil.diffNodes({a:"foo"},{a:"foo",b:"bar"}).should.be.true(); - }); - it('spots removed property', function() { - flowUtil.diffNodes({a:"foo",b:"bar"},{a:"foo"}).should.be.true(); - }); - - - }); - - describe('#parseConfig',function() { - - it('parses a single-tab flow', function() { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t1",type:"tab"} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a single-tab flow with global config node', function() { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",foo:"cn", wires:[]}, - {id:"cn",type:"test"}, - {id:"t1",type:"tab"} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"},"t1":{"id":"t1","type":"tab"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a multi-tab flow', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"t2",type:"tab"}, - {id:"t2-1",x:10,y:10,z:"t2",type:"test",wires:[]} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"t2":{"id":"t2","type":"tab"},"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}},"t2":{"id":"t2","type":"tab","subflows":{},"configs":{},"nodes":{"t2-1":{"id":"t2-1","x":10,"y":10,"z":"t2","type":"test","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a subflow flow', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"subflow:sf1",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",x:10,y:10,z:"sf1",type:"test",wires:[]} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[]},"sf1":{"id":"sf1","type":"subflow"},"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"subflows":{"sf1":{"id":"sf1","type":"subflow","configs":{},"nodes":{"sf1-1":{"id":"sf1-1","x":10,"y":10,"z":"sf1","type":"test","wires":[]}},"instances":[{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}]}},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"subflow:sf1","wires":[],"subflow":"sf1"}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a flow with a missing type', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"sf1",wires:[]}, - {id:"t1-2",x:10,y:10,z:"t1",type:"missing",wires:[]}, - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - parsedConfig.missingTypes.should.eql(['missing']); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},"t1-2":{"id":"t1-2","x":10,"y":10,"z":"t1","type":"missing","wires":[]}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"sf1","wires":[]},'t1-2': { id: 't1-2', x: 10, y: 10, z: 't1', type: 'missing', wires: [] }}}},"groups":{},"missingTypes":["missing"]}; - redUtil.compareObjects(parsedConfig,expectedConfig).should.be.true(); - }); - - it('parses a flow with a missing flow', function() { - var originalConfig = [ - {id:"t1-1",x:10,y:10,z:"t1",type:"test",foo:"cn", wires:[]}, - {id:"cn",type:"test"}, - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]},"cn":{"id":"cn","type":"test"}},"subflows":{},"configs":{"cn":{"id":"cn","type":"test","_users":["t1-1"]}},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","foo":"cn","wires":[]}}}},"groups":{},"missingTypes":[]}; - parsedConfig.should.eql(expectedConfig); - }); - - it('parses a flow including a group', function() { - var originalConfig = [ - {id:"t1",type:"tab"}, - {id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]}, - {id:"g1",type:"group",z:"t1"} - ]; - var parsedConfig = flowUtil.parseConfig(originalConfig); - var expectedConfig = {"allNodes":{"t1":{"id":"t1","type":"tab"},"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]},"g1":{"id":"g1","type":"group","z":"t1"}},"subflows":{},"configs":{},"flows":{"t1":{"id":"t1","type":"tab","subflows":{},"configs":{},"nodes":{"t1-1":{"id":"t1-1","x":10,"y":10,"z":"t1","type":"test","wires":[]}}}},"groups":{"g1":{"id":"g1","type":"group","z":"t1"}},"missingTypes":[]} - parsedConfig.should.eql(expectedConfig); - }); - - }); - - describe('#diffConfigs', function() { - - it('handles an identical configuration', function() { - var config = [{id:"123",type:"test",foo:"a",wires:[]}]; - - var originalConfig = flowUtil.parseConfig(clone(config)); - var changedConfig = flowUtil.parseConfig(clone(config)); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.have.length(0); - }); - - it('identifies nodes with changed properties, including downstream linked', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[[1]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[0].foo = "b"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.eql(["1"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.eql(["2"]); - - }); - it('identifies nodes with changed properties, including upstream linked', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].bar = "c"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.eql(["2"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.eql(["1"]); - }); - - it('identifies nodes with changed credentials, including downstream linked', function() { - var config = [{id:"1",type:"test",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[0].credentials = {}; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.eql(["1"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.should.eql(["2"]); - }); - - it('identifies nodes with changed wiring', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].wires[0][0] = "3"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.eql(["2"]); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('identifies nodes with changed wiring - second connection added', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"2",type:"test",bar:"b",wires:[["1"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].wires[0].push("1"); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.eql(["2"]); - diffResult.linked.sort().should.eql(["1"]); - }); - - it('identifies nodes with changed wiring - node connected', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[["2"]]},{id:"2",type:"test",bar:"b",wires:[[]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig[1].wires.push("3"); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.eql(["2"]); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('identifies new nodes', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig.push({id:"2",type:"test",bar:"b",wires:[["1"]]}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.eql(["2"]); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1"]); - }); - - it('identifies deleted nodes', function() { - var config = [{id:"1",type:"test",foo:"a",wires:[["2"]]},{id:"2",type:"test",bar:"b",wires:[["3"]]},{id:"3",type:"test",foo:"a",wires:[]}]; - var newConfig = clone(config); - newConfig.splice(1,1); - newConfig[0].wires = []; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.eql(["2"]); - diffResult.rewired.should.eql(["1"]); - diffResult.linked.sort().should.eql(["3"]); - }); - - it('identifies config nodes changes, node->config', function() { - var config = [ - {id:"1",type:"test",foo:"configNode",wires:[["2"]]}, - {id:"2",type:"test",bar:"b",wires:[["3"]]}, - {id:"3",type:"test",foo:"a",wires:[]}, - {id:"configNode",type:"testConfig"} - ]; - var newConfig = clone(config); - newConfig[3].foo = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(["1","configNode"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["2","3"]); - }); - - it('identifies config nodes changes, node->config->config', function() { - var config = [ - {id:"1",type:"test",foo:"configNode1",wires:[["2"]]}, - {id:"2",type:"test",bar:"b",wires:[["3"]]}, - {id:"3",type:"test",foo:"a",wires:[]}, - {id:"configNode1",foo:"configNode2",type:"testConfig"}, - {id:"configNode2",type:"testConfig"} - ]; - var newConfig = clone(config); - newConfig[4].foo = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(["1","configNode1","configNode2"]); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["2","3"]); - }); - - it('marks a parent subflow as changed for an internal property change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",foo:"a",wires:[]}, - {id:"4",type:"subflow:sf1",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[4].foo = "b"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', '4', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - - - }); - - it('marks a parent subflow as changed for an internal wiring change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[4].wires = [["sf1-2"]]; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal node add', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig.push({id:"sf1-3",z:"sf1",type:"test",wires:[]}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - - }); - - it('marks a parent subflow as changed for an internal node delete', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig.splice(5,1); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(1); - diffResult.removed.sort().should.eql(['sf1-2']); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - input removed', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].in[0].wires = []; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - input added', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].in[0].wires.push({"id":"sf1-2"}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - output added', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].out[0].wires.push({"id":"sf1-2","port":0}); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow wiring change - output removed', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow","in": [{"wires": [{"id": "sf1-1"}]}],"out": [{"wires": [{"id": "sf1-2","port": 0}]}]}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[3].out[0].wires = []; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for a global config node change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf1-1",z:"sf1",prop:"configNode",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"test",wires:[]}, - {id:"configNode",a:"foo",type:"test",wires:[]} - ]; - - var newConfig = clone(config); - newConfig[6].a = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', "configNode", 'sf1']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - it('marks a parent subflow as changed for an internal subflow instance change', function() { - var config = [ - {id:"1",type:"test",wires:[["2"]]}, - {id:"2",type:"subflow:sf1",wires:[["3"]]}, - {id:"3",type:"test",wires:[]}, - {id:"sf1",type:"subflow"}, - {id:"sf2",type:"subflow"}, - {id:"sf1-1",z:"sf1",type:"test",wires:[]}, - {id:"sf1-2",z:"sf1",type:"subflow:sf2",wires:[]}, - {id:"sf2-1",z:"sf2",type:"test",wires:[]}, - {id:"sf2-2",z:"sf2",type:"test",wires:[]}, - ]; - - var newConfig = clone(config); - newConfig[8].a = "bar"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.sort().should.eql(['2', 'sf1', 'sf2']); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - diffResult.linked.sort().should.eql(["1","3"]); - }); - - - it('ignores tab changes that are immaterial', function() { - var config = [{id:"1",type:"tab",label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"}]; - var newConfig = clone(config); - newConfig[0].label = "barney"; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - }); - - - it('marks a deleted tab as removed', function() { - var config = [{id:"f1",type:"tab",label:"fred"},{id:"n1",type:"test",bar:"b",wires:[["1"]],z:"f1"}, - {id:"f2",type:"tab",label:"fred"},{id:"n2",type:"test",bar:"b",wires:[["1"]],z:"f2"}]; - var newConfig = clone(config); - newConfig = newConfig.slice(0,2); - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.sort().should.eql(['f2', 'n2']); - diffResult.rewired.should.have.length(0); - }); - - it('marks all nodes as added when tab state changes disabled to enabled', function() { - var config = [{id:"1",type:"tab",disabled:true,label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"},{id:"3",type:"test"}]; - var newConfig = clone(config); - newConfig[0].disabled = false; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(2); - diffResult.added.sort().should.eql(["1","2"]); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(0); - diffResult.rewired.should.have.length(0); - }); - it('marks all nodes as removed when tab state changes enabled to disabled', function() { - var config = [{id:"1",type:"tab",disabled:false,label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"},{id:"3",type:"test"}]; - var newConfig = clone(config); - newConfig[0].disabled = true; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(0); - diffResult.removed.should.have.length(2); - diffResult.removed.sort().should.eql(["1","2"]); - diffResult.rewired.should.have.length(0); - }); - - it('marks a node as removed when its state changes enabled to disabled', function() { - var config = [{id:"1",type:"tab",disabled:false,label:"fred"},{id:"2",type:"test",bar:"b",wires:[["1"]],z:"1"},{id:"3",type:"test"}]; - var newConfig = clone(config); - newConfig[1].d = true; - - var originalConfig = flowUtil.parseConfig(config); - var changedConfig = flowUtil.parseConfig(newConfig); - - originalConfig.missingTypes.should.have.length(0); - - var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); - - diffResult.added.should.have.length(0); - diffResult.changed.should.have.length(2); - diffResult.changed.sort().should.eql(["1","2"]); - diffResult.removed.should.have.length(1); - diffResult.removed.sort().should.eql(["2"]); - diffResult.rewired.should.have.length(0); - }); - - }); -}); diff --git a/test/unit/@node-red/runtime/lib/index_spec.js b/test/unit/@node-red/runtime/lib/index_spec.js deleted file mode 100644 index 9041ba66e..000000000 --- a/test/unit/@node-red/runtime/lib/index_spec.js +++ /dev/null @@ -1,250 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var api = NR_TEST_UTILS.require("@node-red/runtime/lib/api"); -var runtime = NR_TEST_UTILS.require("@node-red/runtime"); - -var redNodes = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes"); -var storage = NR_TEST_UTILS.require("@node-red/runtime/lib/storage"); -var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/settings"); -var util = NR_TEST_UTILS.require("@node-red/util"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; -var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - -describe("runtime", function() { - afterEach(function() { - if (console.log.restore) { - console.log.restore(); - } - }) - - before(function() { - process.env.NODE_RED_HOME = NR_TEST_UTILS.resolve("node-red"); - }); - after(function() { - delete process.env.NODE_RED_HOME; - }); - function mockUtil(metrics) { - sinon.stub(log,"log").callsFake(function(){}) - sinon.stub(log,"warn").callsFake(function(){}) - sinon.stub(log,"info").callsFake(function(){}) - sinon.stub(log,"trace").callsFake(function(){}) - sinon.stub(log,"metric").callsFake(function(){ return !!metrics }) - sinon.stub(log,"_").callsFake(function(){ return "abc"}) - sinon.stub(i18n,"registerMessageCatalog").callsFake(function(){ return Promise.resolve()}) - } - function unmockUtil() { - log.log.restore && log.log.restore(); - log.warn.restore && log.warn.restore(); - log.info.restore && log.info.restore(); - log.trace.restore && log.trace.restore(); - log.metric.restore && log.metric.restore(); - log._.restore && log._.restore(); - i18n.registerMessageCatalog.restore && i18n.registerMessageCatalog.restore(); - } - describe("init", function() { - beforeEach(function() { - sinon.stub(log,"init").callsFake(function() {}); - sinon.stub(settings,"init").callsFake(function() {}); - sinon.stub(redNodes,"init").callsFake(function() {}) - mockUtil(); - }); - afterEach(function() { - log.init.restore(); - settings.init.restore(); - redNodes.init.restore(); - unmockUtil(); - }) - - it("initialises components", function() { - runtime.init({testSettings: true, httpAdminRoot:"/"}); - settings.init.called.should.be.true(); - redNodes.init.called.should.be.true(); - }); - - it("returns version", function() { - runtime.init({testSettings: true, httpAdminRoot:"/"}); - return runtime.version().then(version => { - /^\d+\.\d+\.\d+(-.*)?$/.test(version).should.be.true(); - }); - - - }) - }); - - describe("start",function() { - var storageInit; - var settingsLoad; - var redNodesInit; - var redNodesLoad; - var redNodesCleanModuleList; - var redNodesGetNodeList; - var redNodesLoadFlows; - var redNodesStartFlows; - var redNodesLoadContextsPlugin; - - beforeEach(function() { - storageInit = sinon.stub(storage,"init").callsFake(function(settings) {return Promise.resolve();}); - redNodesInit = sinon.stub(redNodes,"init").callsFake(function() {}); - redNodesLoad = sinon.stub(redNodes,"load").callsFake(function() {return Promise.resolve()}); - redNodesCleanModuleList = sinon.stub(redNodes,"cleanModuleList").callsFake(function(){}); - redNodesLoadFlows = sinon.stub(redNodes,"loadFlows").callsFake(function() {return Promise.resolve()}); - redNodesStartFlows = sinon.stub(redNodes,"startFlows").callsFake(function() {}); - redNodesLoadContextsPlugin = sinon.stub(redNodes,"loadContextsPlugin").callsFake(function() {return Promise.resolve()}); - mockUtil(); - }); - afterEach(function() { - storageInit.restore(); - redNodesInit.restore(); - redNodesLoad.restore(); - redNodesGetNodeList.restore(); - redNodesCleanModuleList.restore(); - redNodesLoadFlows.restore(); - redNodesStartFlows.restore(); - redNodesLoadContextsPlugin.restore(); - unmockUtil(); - }); - it("reports errored/missing modules",function(done) { - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function(cb) { - return [ - { err:"errored",name:"errName" }, // error - { module:"module",enabled:true,loaded:false,types:["typeA","typeB"]} // missing - ].filter(cb); - }); - runtime.init({testSettings: true, httpAdminRoot:"/", load:function() { return Promise.resolve();}}); - // sinon.stub(console,"log"); - runtime.start().then(function() { - // console.log.restore(); - try { - storageInit.calledOnce.should.be.true(); - redNodesInit.calledOnce.should.be.true(); - redNodesLoad.calledOnce.should.be.true(); - redNodesLoadFlows.calledOnce.should.be.true(); - - log.warn.calledWithMatch("Failed to register 1 node type"); - log.warn.calledWithMatch("Missing node modules"); - log.warn.calledWithMatch(" - module: typeA, typeB"); - redNodesCleanModuleList.calledOnce.should.be.true(); - done(); - } catch(err) { - done(err); - } - }).catch(err=>{done(err)}); - }); - it("initiates load of missing modules",function(done) { - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function(cb) { - return [ - { err:"errored",name:"errName" }, // error - { err:"errored",name:"errName" }, // error - { module:"module",enabled:true,loaded:false,types:["typeA","typeB"]}, // missing - { module:"node-red",enabled:true,loaded:false,types:["typeC","typeD"]} // missing - ].filter(cb); - }); - var serverInstallModule = sinon.stub(redNodes,"installModule").callsFake(function(name) { return Promise.resolve({nodes:[]});}); - runtime.init({testSettings: true, autoInstallModules:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}}); - sinon.stub(console,"log"); - runtime.start().then(function() { - console.log.restore(); - try { - log.warn.calledWithMatch("Failed to register 2 node types"); - log.warn.calledWithMatch("Missing node modules"); - log.warn.calledWithMatch(" - module: typeA, typeB"); - log.warn.calledWithMatch(" - node-red: typeC, typeD"); - redNodesCleanModuleList.calledOnce.should.be.false(); - serverInstallModule.calledOnce.should.be.true(); - serverInstallModule.calledWithMatch("module"); - done(); - } catch(err) { - done(err); - } finally { - serverInstallModule.restore(); - } - }).catch(err=>{done(err)}); - }); - it("reports errored modules when verbose is enabled",function(done) { - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function(cb) { - return [ - { err:"errored",name:"errName" } // error - ].filter(cb); - }); - runtime.init({testSettings: true, verbose:true, httpAdminRoot:"/", load:function() { return Promise.resolve();}}); - sinon.stub(console,"log"); - runtime.start().then(function() { - console.log.restore(); - try { - log.warn.neverCalledWithMatch("Failed to register 1 node type"); - log.warn.calledWithMatch("[errName] errored"); - done(); - } catch(err) { - done(err); - } - }).catch(err=>{done(err)}); - }); - - it("reports runtime metrics",function(done) { - var stopFlows = sinon.stub(redNodes,"stopFlows").callsFake(function() { return Promise.resolve();} ); - redNodesGetNodeList = sinon.stub(redNodes,"getNodeList").callsFake(function() {return []}); - unmockUtil(); - mockUtil(true); - runtime.init( - {testSettings: true, runtimeMetricInterval:200, httpAdminRoot:"/", load:function() { return Promise.resolve();}}, - {}, - undefined); - // sinon.stub(console,"log"); - runtime.start().then(function() { - // console.log.restore(); - setTimeout(function() { - try { - log.log.args.should.have.lengthOf(3); - log.log.args[0][0].should.have.property("event","runtime.memory.rss"); - log.log.args[1][0].should.have.property("event","runtime.memory.heapTotal"); - log.log.args[2][0].should.have.property("event","runtime.memory.heapUsed"); - done(); - } catch(err) { - done(err); - } finally { - runtime.stop(); - stopFlows.restore(); - } - },300); - }).catch(err=>{done(err)}); - }); - - - }); - - it("stops components", function(done) { - var stopFlows = sinon.stub(redNodes,"stopFlows").callsFake(function() { return Promise.resolve();} ); - var closeContextsPlugin = sinon.stub(redNodes,"closeContextsPlugin").callsFake(function() { return Promise.resolve();} ); - runtime.stop().then(function(){ - stopFlows.called.should.be.true(); - closeContextsPlugin.called.should.be.true(); - stopFlows.restore(); - closeContextsPlugin.restore(); - done(); - }).catch(function(err){ - stopFlows.restore(); - closeContextsPlugin.restore(); - return done(err) - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/library/examples_spec.js b/test/unit/@node-red/runtime/lib/library/examples_spec.js deleted file mode 100644 index d7f466d83..000000000 --- a/test/unit/@node-red/runtime/lib/library/examples_spec.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var fs = require("fs"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - -describe("runtime/library/examples", function() { - describe("getEntry", function() { - before(function() { - examplesLibrary.init({ - log: mockLog, - storage: { - getLibraryEntry: function(type,path) { - return Promise.resolve({type,path}); - }, - getFlow: function(path) { - return Promise.resolve({path}); - } - }, - nodes: { - getNodeExampleFlows: function() { - return { - "test-module": { - f: ["abc"] - }, - "@scope/test-module": { - f: ["abc","throw"] - } - - } - }, - getNodeExampleFlowPath: function(module,entryPath) { - if (module === "unknown") { - return null; - } - return "/tmp/"+module+"/"+entryPath; - } - } - }); - sinon.stub(fs,"readFile").callsFake(function(path,opts,callback) { - if (path === "/tmp/test-module/abc") { - callback(null,"Example flow result"); - } else if (path === "/tmp/@scope/test-module/abc") { - callback(null,"Example scope flow result"); - } else if (path === "/tmp/test-module/throw") { - throw new Error("Instant error") - } else { - callback(new Error("Unexpected path:"+path)) - } - }) - }); - after(function() { - fs.readFile.restore(); - }) - - it ('returns a flow example entry', function(done) { - examplesLibrary.getEntry("flows","test-module/abc").then(function(result) { - result.should.eql("Example flow result"); - done(); - }).catch(done); - }); - - it ('returns a flow example listing - top level', function(done) { - examplesLibrary.getEntry("flows","").then(function(result) { - result.should.eql([ 'test-module', '@scope/test-module' ]) - done(); - }).catch(done); - }); - it ('returns a flow example listing - in module', function(done) { - examplesLibrary.getEntry("flows","test-module").then(function(result) { - result.should.eql([{ fn: 'abc' }]) - done(); - }).catch(done); - }); - it ('returns a flow example listing - in scoped module', function(done) { - examplesLibrary.getEntry("flows","@scope/test-module").then(function(result) { - result.should.eql([{ fn: 'abc' }, {fn: 'throw'}]) - done(); - }).catch(done); - }); - it ('returns a flow example entry from scoped module', function(done) { - examplesLibrary.getEntry("flows","@scope/test-module/abc").then(function(result) { - result.should.eql("Example scope flow result"); - done(); - }).catch(done); - }); - it ('returns an error for unknown flow example entry', function(done) { - examplesLibrary.getEntry("flows","unknown/abc").then(function(result) { - done(new Error("No error thrown")) - }).catch(function(err) { - err.should.have.property("code","not_found"); - done(); - }); - }); - it ('returns an error for file load error - async', function(done) { - examplesLibrary.getEntry("flows","test-module/unknown").then(function(result) { - done(new Error("No error thrown")) - }).catch(function(err) { - done(); - }); - }); - it ('returns an error for file load error - sync', function(done) { - examplesLibrary.getEntry("flows","test-module/throw").then(function(result) { - done(new Error("No error thrown")) - }).catch(function(err) { - done(); - }); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/library/index_spec.js b/test/unit/@node-red/runtime/lib/library/index_spec.js deleted file mode 100644 index 2499124c7..000000000 --- a/test/unit/@node-red/runtime/lib/library/index_spec.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -const should = require("should"); -const sinon = require("sinon"); - -const NR_TEST_UTILS = require("nr-test-utils"); -const library = NR_TEST_UTILS.require("@node-red/runtime/lib/library/index") -const localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local") -const examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples") -const events = NR_TEST_UTILS.require("@node-red/util/lib/events") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - - -describe("runtime/library", function() { - before(function() { - sinon.stub(localLibrary,"getEntry").callsFake(function(type,path) { - return Promise.resolve({ - library: "local", - type:type, - path:path - }) - }); - sinon.stub(localLibrary,"saveEntry").callsFake(function(type, path, meta, body) { - return Promise.resolve({ - library: "local", - type:type, - path:path, - meta:meta, - body:body - }) - }); - sinon.stub(examplesLibrary,"getEntry").callsFake(function(type,path) { - return Promise.resolve({ - library: "_examples_", - type:type, - path:path - }) - }); - }); - after(function() { - localLibrary.getEntry.restore(); - localLibrary.saveEntry.restore(); - examplesLibrary.getEntry.restore(); - }) - describe("register", function() { - // it("throws error for duplicate type", function() { - // library.init({}); - // library.register("unknown","/abc"); - // should(()=>{library.register("unknown","/abc")} ).throw(); - // }) - }) - - describe("getLibraries", function() { - before(function() { - library.init({}); - }); - it('returns the default and examples libraries', function() { - const libs = library.getLibraries(); - libs.should.have.length(2); - libs[0].should.have.property('id', 'local'); - libs[0].should.have.property('label','editor:library.types.local'); - libs[0].should.have.property("user", false); - libs[0].should.have.property('icon', 'font-awesome/fa-hdd-o'); - - libs[1].should.have.property('id', 'examples'); - libs[1].should.have.property('label','editor:library.types.examples'); - libs[1].should.have.property("user", false); - libs[1].should.have.property('icon', 'font-awesome/fa-life-ring'); - libs[1].should.have.property('readOnly', true); - libs[1].should.have.property('types', ['flows']); - }); - - it('returns the libraries from settings', function() { - library.init({ - plugins: { - getPlugin: id => { return { - id: "test-library-plugin", - type: "node-red-library-source", - class: function() {} - } - } - }, - settings: { - editorTheme: { - library: { - sources: [ - {id: "test-plugin-id", type:"test-library-plugin"} - ] - } - } - } - }); - let libs = library.getLibraries(); - libs.should.have.length(2); - - events.emit("registry:plugin-added","test-library-plugin" ) - - libs = library.getLibraries(); - libs.should.have.length(3); - libs[2].should.have.property('id', 'test-plugin-id'); - libs[2].should.have.property("user", false); - }); - }) - - describe("getEntry", function() { - before(function() { - library.init({}); - }); - it('throws error for unregistered type', function() { - should(()=>{library.getEntry("local","unknown","/abc")} ).throw(); - }); - it('throws error for unknown library', function() { - should(()=>{library.getEntry("unknown","unknown","/abc")} ).throw(); - }); - - it('returns a registered non-flow entry', function(done) { - library.register("test-module","test-type"); - library.getEntry("local","test-type","/abc").then(function(result) { - result.should.have.property("library","local") - result.should.have.property("type","test-type") - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - - it ('returns a flow entry', function(done) { - library.getEntry("local","flows","/abc").then(function(result) { - result.should.have.property("library","local") - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - - it ('returns a flow example entry', function(done) { - library.getEntry("examples","flows","/test-module/abc").then(function(result) { - result.should.have.property("library","_examples_") - result.should.have.property("path","/test-module/abc") - done(); - }).catch(done); - }); - }); - - describe("saveEntry", function() { - before(function() { - library.init({}); - }); - it('throws error for unknown library', function() { - should(()=>{library.saveEntry("unknown","unknown","/abc",{id:"meta"},{id:"body"})} ).throw(); - }); - it('throws error for unregistered type', function() { - should(()=>{library.saveEntry("local","unknown","/abc",{id:"meta"},{id:"body"})} ).throw(); - }); - it('throws error for save to readonly library', function() { - should(()=>{library.saveEntry("_examples_","unknown","/abc",{id:"meta"},{id:"body"})} ).throw(); - }); - it('saves a flow entry', function(done) { - library.saveEntry('local','flows','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("path","/abc"); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - it('saves a non-flow entry', function(done) { - library.register("test-module","test-type"); - library.saveEntry('local','test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("type","test-type"); - result.should.have.property("path","/abc"); - result.should.have.property("meta",{id:"meta"}); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - - }); -}); diff --git a/test/unit/@node-red/runtime/lib/library/local_spec.js b/test/unit/@node-red/runtime/lib/library/local_spec.js deleted file mode 100644 index 965e5c87a..000000000 --- a/test/unit/@node-red/runtime/lib/library/local_spec.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local") - -var mockLog = { - log: sinon.stub(), - debug: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub(), - info: sinon.stub(), - metric: sinon.stub(), - audit: sinon.stub(), - _: function() { return "abc"} -} - -describe("runtime/library/local", function() { - - describe("getEntry", function() { - before(function() { - localLibrary.init({ - log: mockLog, - storage: { - getLibraryEntry: function(type,path) { - return Promise.resolve({type,path}); - } - } - }); - }); - - it('returns a registered non-flow entry', function(done) { - localLibrary.getEntry("test-type","/abc").then(function(result) { - result.should.have.property("type","test-type") - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - - it ('returns a flow entry', function(done) { - localLibrary.getEntry("flows","/abc").then(function(result) { - result.should.have.property("path","/abc") - done(); - }).catch(done); - }); - }); - - describe("saveEntry", function() { - before(function() { - localLibrary.init({ - log: mockLog, - storage: { - saveLibraryEntry: function(type, path, meta, body) { - return Promise.resolve({type,path,meta,body}) - } - } - }); - }); - it('saves a flow entry', function(done) { - localLibrary.saveEntry('flows','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("path","/abc"); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - it('saves a non-flow entry', function(done) { - localLibrary.saveEntry('test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) { - result.should.have.property("type","test-type"); - result.should.have.property("path","/abc"); - result.should.have.property("meta",{id:"meta"}); - result.should.have.property("body",{id:"body"}); - done(); - }).catch(done); - }) - - }); -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/Node_spec.js b/test/unit/@node-red/runtime/lib/nodes/Node_spec.js deleted file mode 100644 index 3fe676f1e..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/Node_spec.js +++ /dev/null @@ -1,809 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); -var NR_TEST_UTILS = require("nr-test-utils"); -var RedNode = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); -var Log = NR_TEST_UTILS.require("@node-red/util").log; -var hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); - -describe('Node', function() { - describe('#constructor',function() { - it('is called with an id and a type',function() { - var n = new RedNode({id:'123',type:'abc'}); - n.should.have.property('id','123'); - n.should.have.property('type','abc'); - n.should.not.have.property('name'); - n.wires.should.be.empty(); - }); - - it('is called with an id, a type and a name',function() { - var n = new RedNode({id:'123',type:'abc',name:'barney'}); - n.should.have.property('id','123'); - n.should.have.property('type','abc'); - n.should.have.property('name','barney'); - n.wires.should.be.empty(); - }); - - it('is called with an id, a type and some wires',function() { - var n = new RedNode({id:'123',type:'abc',wires:['123','456']}); - n.should.have.property('id','123'); - n.should.have.property('type','abc'); - n.should.not.have.property('name'); - n.wires.should.have.length(2); - }); - - }); - - describe('#close', function() { - it('emits close event when closed',function(done) { - var n = new RedNode({id:'123',type:'abc'}); - n.on('close',function() { - done(); - }); - n.close(); - }); - - it('returns a promise when provided a callback with a done parameter',function(testdone) { - var n = new RedNode({id:'123',type:'abc'}); - n.on('close',function(done) { - setTimeout(function() { - done(); - },50); - }); - var p = n.close(); - should.exist(p); - p.then(function() { - testdone(); - }); - }); - it('accepts a callback with "removed" and "done" parameters', function(testdone) { - var n = new RedNode({id:'123',type:'abc'}); - var receivedRemoved; - n.on('close',function(removed,done) { - receivedRemoved = removed; - setTimeout(function() { - done(); - },50); - }); - var p = n.close(true); - should.exist(p); - (receivedRemoved).should.be.true(); - p.then(function() { - testdone(); - }); - }) - it('allows multiple close handlers to be registered',function(testdone) { - var n = new RedNode({id:'123',type:'abc'}); - var callbacksClosed = 0; - n.on('close',function(done) { - setTimeout(function() { - callbacksClosed++; - done(); - },50); - }); - n.on('close',function(done) { - setTimeout(function() { - callbacksClosed++; - done(); - },75); - }); - n.on('close',function() { - callbacksClosed++; - }); - var p = n.close(); - should.exist(p); - p.then(function() { - callbacksClosed.should.eql(3); - testdone(); - }).catch(function(e) { - testdone(e); - }); - }); - }); - - - describe('#receive', function() { - it('emits input event when called', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"hello world"}; - n.on('input',function(msg) { - should.deepEqual(msg,message); - done(); - }); - n.receive(message); - }); - - it('writes metric info with undefined msg', function(done){ - var n = new RedNode({id:'123',type:'abc'}); - n.on('input',function(msg) { - (typeof msg).should.not.be.equal("undefined"); - (typeof msg._msgid).should.not.be.equal("undefined"); - done(); - }); - n.receive(); - }); - - it('writes metric info with null msg', function(done){ - var n = new RedNode({id:'123',type:'abc'}); - n.on('input',function(msg) { - (typeof msg).should.not.be.equal("undefined"); - (typeof msg._msgid).should.not.be.equal("undefined"); - done(); - }); - n.receive(null); - }); - - it('handles thrown errors', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - sinon.stub(n,"error").callsFake(function(err,msg) {}); - var message = {payload:"hello world"}; - n.on('input',function(msg) { - throw new Error("test error"); - }); - n.receive(message); - setTimeout(function() { - n.error.called.should.be.true(); - n.error.firstCall.args[1].should.equal(message); - done(); - },50); - }); - - it('calls parent flow handleComplete when callback provided', function(done) { - var n = new RedNode({id:'123',type:'abc', _flow: { - handleComplete: function(node,msg) { - try { - msg.should.deepEqual(message); - done(); - } catch(err) { - done(err); - } - } - }}); - - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(); - }); - n.receive(message); - }); - - it('triggers onComplete hook when done callback provided', function(done) { - var handleCompleteCalled = false; - var hookCalled = false; - var n = new RedNode({id:'123',type:'abc', _flow: { - handleComplete: function(node,msg) { - handleCompleteCalled = true; - } - }}); - var hookError; - hooks.add("onComplete",function(completeEvent) { - hookCalled = true; - try { - handleCompleteCalled.should.be.false("onComplete should be called before handleComplete") - should.not.exist(completeEvent.error); - completeEvent.msg.should.deepEqual(message); - completeEvent.node.id.should.eql("123"); - completeEvent.node.node.should.equal(n); - } catch(err) { - hookError = err; - } - }) - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(); - }); - n.receive(message); - setTimeout(function() { - if (hookError) { - done(hookError); - return - } - try { - hookCalled.should.be.true("onComplete hook should be called"); - handleCompleteCalled.should.be.true("handleComplete should be called"); - done(); - } catch(err) { - done(err); - } - }) - }); - - it('triggers onComplete hook when done callback provided - with error', function(done) { - var handleCompleteCalled = false; - var hookCalled = false; - var errorReported = false; - var n = new RedNode({id:'123',type:'abc', _flow: { - handleComplete: function(node,msg) { - handleCompleteCalled = true; - } - }}); - var hookError; - hooks.add("onComplete",function(completeEvent) { - hookCalled = true; - try { - handleCompleteCalled.should.be.false("onComplete should be called before handleComplete") - should.exist(completeEvent.error); - completeEvent.error.toString().should.equal("Error: test error") - completeEvent.msg.should.deepEqual(message); - completeEvent.node.id.should.eql("123"); - completeEvent.node.node.should.equal(n); - } catch(err) { - hookError = err; - } - }) - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(new Error("test error")); - }); - n.error = function(err,msg) { - errorReported = true; - } - n.receive(message); - setTimeout(function() { - if (hookError) { - done(hookError); - return - } - try { - hookCalled.should.be.true("onComplete hook should be called"); - handleCompleteCalled.should.be.false("handleComplete should not be called"); - done(); - } catch(err) { - done(err); - } - }) - }); - it('logs error if callback provides error', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - sinon.stub(n,"error").callsFake(function(err,msg) {}); - - var message = {payload:"hello world"}; - n.on('input',function(msg, nodeSend, nodeDone) { - nodeDone(new Error("test error")); - setTimeout(function() { - try { - n.error.called.should.be.true(); - n.error.firstCall.args[0].toString().should.equal("Error: test error"); - n.error.firstCall.args[1].should.equal(message); - done(); - } catch(err) { - done(err); - } - },50); - }); - n.receive(message); - }); - it("triggers hooks when receiving a message", function(done) { - var hookErrors = []; - var messageReceived = false; - var hooksCalled = []; - hooks.add("onReceive", function(receiveEvent) { - hooksCalled.push("onReceive") - try { - messageReceived.should.be.false("Message should not have been received before onReceive") - receiveEvent.msg.should.be.exactly(message); - receiveEvent.destination.id.should.equal("123") - receiveEvent.destination.node.should.equal(n) - } catch(err) { - hookErrors.push(err); - } - }) - hooks.add("postReceive", function(receiveEvent) { - hooksCalled.push("postReceive") - try { - messageReceived.should.be.true("Message should have been received before postReceive") - receiveEvent.msg.should.be.exactly(message); - receiveEvent.destination.id.should.equal("123") - receiveEvent.destination.node.should.equal(n) - } catch(err) { - hookErrors.push(err); - } - - }) - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"hello world"}; - n.on('input',function(msg) { - messageReceived = true; - try { - should.strictEqual(this,n); - hooksCalled.should.eql(["onReceive"]) - should.deepEqual(msg,message); - } catch(err) { - hookErrors.push(err) - } - }); - n.receive(message); - setTimeout(function() { - hooks.clear(); - if (hookErrors.length > 0) { - done(hookErrors[0]) - } else { - done(); - } - },10); - }); - describe("errors thrown by hooks are reported", function() { - before(function() { - hooks.add("onReceive",function(recEvent) { - if (recEvent.msg.payload === "trigger-onReceive") { - throw new Error("onReceive Error") - } - }) - hooks.add("postReceive",function(recEvent) { - if (recEvent.msg.payload === "trigger-postReceive") { - throw new Error("postReceive Error") - } - }) - }) - after(function() { - hooks.clear(); - }) - function testHook(hook, msgExpected, done) { - var messageReceived = false; - var errorReceived; - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"trigger-"+hook}; - n.on('input',function(msg) { - messageReceived = true; - }); - n.error = function (err) { - errorReceived = err; - } - - n.receive(message); - - setTimeout(function() { - try { - messageReceived.should.equal(msgExpected,`Hook ${hook} messageReceived expected ${msgExpected} actual ${messageReceived}`); - should.exist(errorReceived); - errorReceived.toString().should.containEql(hook) - done() - } catch(err) { - done(err); - } - },10); - } - it("onReceive", function(done) { testHook("onReceive", false, done)}) - it("postReceive", function(done) { testHook("postReceive", true, done)}) - }) - }); - - describe("hooks can halt receive", function() { - before(function() { - hooks.add("onReceive",function(recEvent) { - if (recEvent.msg.payload === "trigger-onReceive") { - return false; - } - }) - }) - after(function() { - hooks.clear(); - }) - - function testHook(hook, msgExpected, done) { - var messageReceived = false; - var errorReceived; - var n = new RedNode({id:'123',type:'abc'}); - var message = {payload:"trigger-"+hook}; - n.on('input',function(msg) { - messageReceived = true; - }); - n.error = function (err) { - errorReceived = err; - } - - n.receive(message); - - setTimeout(function() { - try { - messageReceived.should.equal(msgExpected,`Hook ${hook} messageReceived expected ${msgExpected} actual ${messageReceived}`); - should.not.exist(errorReceived); - done() - } catch(err) { - done(err); - } - },10); - } - it("onReceive", function(done) { testHook("onReceive", false, done)}) - }) - - - describe('#send', function() { - - it('emits a single message', function(done) { - var flow = { - send: (sendEvents) => { - try { - sendEvents.should.have.length(1); - sendEvents[0].msg.should.equal(message); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}) - done(); - } catch(err) { - done(err); - } - }, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - var message = {payload:"hello world"}; - n1.send(message); - }); - - it('emits a message with callback provided send', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - handleComplete: (node,msg) => {}, - send: (sendEvents) => { - try { - sendEvents.should.have.length(1); - sendEvents[0].msg.should.equal(message); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[0].cloneMessage.should.be.false(); - done(); - } catch(err) { - done(err); - } - }, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - var message = {payload:"hello world"}; - n1.on('input',function(msg,nodeSend,nodeDone) { - nodeSend(msg); - nodeDone(); - }); - n1.receive(message); - }); - - it('emits multiple messages on a single output', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - send: (sendEvents) => { - try { - sendEvents.should.have.length(2); - sendEvents[0].msg.should.equal(messages[0]); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[0].cloneMessage.should.be.false(); - - sendEvents[1].msg.should.equal(messages[1]); - sendEvents[1].destination.should.eql({id:"n2", node: undefined}); - sendEvents[1].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[1].cloneMessage.should.be.true(); - - done(); - } catch(err) { - done(err); - } - }, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - - var messages = [ - {payload:"hello world"}, - {payload:"hello world again"} - ]; - - n1.send([messages]); - }); - - it('emits messages to multiple outputs', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - send: (sendEvents) => { - try { - sendEvents.should.have.length(3); - sendEvents[0].msg.should.equal(messages[0]); - sendEvents[0].destination.should.eql({id:"n2", node: undefined}); - sendEvents[0].source.should.eql({id:"n1", node: n1, port: 0}); - sendEvents[0].cloneMessage.should.be.false(); - should.exist(sendEvents[0].msg._msgid); - sendEvents[1].msg.should.equal(messages[2]); - sendEvents[1].destination.should.eql({id:"n4", node: undefined}); - sendEvents[1].source.should.eql({id:"n1", node: n1, port: 2}) - sendEvents[1].cloneMessage.should.be.true(); - should.exist(sendEvents[1].msg._msgid); - sendEvents[2].msg.should.equal(messages[2]); - sendEvents[2].destination.should.eql({id:"n5", node: undefined}); - sendEvents[2].source.should.eql({id:"n1", node: n1, port: 2}) - sendEvents[2].cloneMessage.should.be.true(); - should.exist(sendEvents[2].msg._msgid); - - sendEvents[0].msg._msgid.should.eql(sendEvents[1].msg._msgid) - sendEvents[1].msg._msgid.should.eql(sendEvents[2].msg._msgid) - - done(); - } catch(err) { - done(err); - } - } - }; - var n1 = new RedNode({_flow:flow, id:'n1',type:'abc',wires:[['n2'],['n3'],['n4','n5']]}); - var n2 = new RedNode({_flow:flow, id:'n2',type:'abc'}); - var n3 = new RedNode({_flow:flow, id:'n3',type:'abc'}); - var n4 = new RedNode({_flow:flow, id:'n4',type:'abc'}); - var n5 = new RedNode({_flow:flow, id:'n5',type:'abc'}); - var messages = [ - {payload:"hello world"}, - null, - {payload:"hello world again"} - ]; - - var rcvdCount = 0; - - n1.send(messages); - }); - - it('emits no messages', function(done) { - var flow = { - handleError: (node,logMessage,msg,reportingNode) => {done(logMessage)}, - getNode: (id) => { return {'n1':n1,'n2':n2}[id]}, - }; - var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[['n2']]}); - var n2 = new RedNode({_flow:flow,id:'n2',type:'abc'}); - - n2.on('input',function(msg) { - should.fail(null,null,"unexpected message"); - }); - - setTimeout(function() { - done(); - }, 200); - - n1.send(); - }); - - // it('emits messages without cloning req or res', function(done) { - // var flow = { - // getNode: (id) => { return {'n1':n1,'n2':n2,'n3':n3}[id]}, - // send: (node,dst,msg) => { setImmediate(function() { flow.getNode(dst).receive(msg) })} - // }; - // var n1 = new RedNode({_flow:flow,id:'n1',type:'abc',wires:[[['n2'],['n3']]]}); - // var n2 = new RedNode({_flow:flow,id:'n2',type:'abc'}); - // var n3 = new RedNode({_flow:flow,id:'n3',type:'abc'}); - // - // var req = {}; - // var res = {}; - // var cloned = {}; - // var message = {payload: "foo", cloned: cloned, req: req, res: res}; - // - // var rcvdCount = 0; - // - // // first message to be sent, so should not be cloned - // n2.on('input',function(msg) { - // should.deepEqual(msg, message); - // msg.cloned.should.be.exactly(message.cloned); - // msg.req.should.be.exactly(message.req); - // msg.res.should.be.exactly(message.res); - // rcvdCount += 1; - // if (rcvdCount == 2) { - // done(); - // } - // }); - // - // // second message to be sent, so should be cloned - // // message uuids wont match since we've cloned - // n3.on('input',function(msg) { - // msg.payload.should.equal(message.payload); - // msg.cloned.should.not.be.exactly(message.cloned); - // msg.req.should.be.exactly(message.req); - // msg.res.should.be.exactly(message.res); - // rcvdCount += 1; - // if (rcvdCount == 2) { - // done(); - // } - // }); - // - // n1.send(message); - // }); - - // it("logs the uuid for all messages sent", function(done) { - // var logHandler = { - // msgIds:[], - // messagesSent: 0, - // emit: function(event, msg) { - // if (msg.event == "node.abc.send" && msg.level == Log.METRIC) { - // this.messagesSent++; - // this.msgIds.push(msg.msgid); - // (typeof msg.msgid).should.not.be.equal("undefined"); - // } - // } - // }; - // - // Log.addHandler(logHandler); - // var flow = { - // getNode: (id) => { return {'n1':sender,'n2':receiver1,'n3':receiver2}[id]}, - // send: (node,dst,msg) => { setImmediate(function() { flow.getNode(dst).receive(msg) })} - // }; - // - // var sender = new RedNode({_flow:flow,id:'n1',type:'abc', wires:[['n2', 'n3']]}); - // var receiver1 = new RedNode({_flow:flow,id:'n2',type:'abc'}); - // var receiver2 = new RedNode({_flow:flow,id:'n3',type:'abc'}); - // sender.send({"some": "message"}); - // setTimeout(function() { - // try { - // logHandler.messagesSent.should.equal(1); - // should.exist(logHandler.msgIds[0]) - // Log.removeHandler(logHandler); - // done(); - // } catch(err) { - // Log.removeHandler(logHandler); - // done(err); - // } - // },50) - // }) - }); - - - describe('#log', function() { - it('produces a log message', function(done) { - var n = new RedNode({id:'123',type:'abc',z:'789', _flow: {log:function(msg) { loginfo = msg;}}}); - var loginfo = {}; - n.log("a log message"); - should.deepEqual({level:Log.INFO, id:n.id, - type:n.type, msg:"a log message",z:'789'}, loginfo); - done(); - }); - it('produces a log message with a name', function(done) { - var n = new RedNode({id:'123', type:'abc', name:"barney", z:'789', _flow: {log:function(msg) { loginfo = msg;}}}); - var loginfo = {}; - n.log("a log message"); - should.deepEqual({level:Log.INFO, id:n.id, name: "barney", - type:n.type, msg:"a log message",z:'789'}, loginfo); - done(); - }); - }); - - describe('#warn', function() { - it('produces a warning message', function(done) { - var n = new RedNode({id:'123',type:'abc',z:'789', _flow: {log:function(msg) { loginfo = msg;}}}); - var loginfo = {}; - n.warn("a warning"); - should.deepEqual({level:Log.WARN, id:n.id, - type:n.type, msg:"a warning",z:'789'}, loginfo); - done(); - }); - }); - - describe('#error', function() { - it('handles a null error message', function(done) { - var flow = { - handleError: sinon.stub(), - log:sinon.stub() - } - var n = new RedNode({_flow:flow, id:'123',type:'abc',z:'789'}); - var message = {a:1}; - n.error(null,message); - - flow.handleError.called.should.be.true(); - flow.handleError.args[0][0].should.eql(n); - flow.handleError.args[0][1].should.eql(""); - flow.handleError.args[0][2].should.eql(message); - - done(); - }); - - it('produces an error message', function(done) { - var flow = { - handleError: sinon.stub(), - log:sinon.stub() - } - var n = new RedNode({_flow:flow, id:'123',type:'abc',z:'789'}); - var message = {a:2}; - - n.error("This is an error",message); - - flow.handleError.called.should.be.true(); - flow.handleError.args[0][0].should.eql(n); - flow.handleError.args[0][1].should.eql("This is an error"); - flow.handleError.args[0][2].should.eql(message); - - done(); - }); - - }); - - describe('#metric', function() { - it('produces a metric message', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var loginfo = {}; - sinon.stub(Log, 'log').callsFake(function(msg) { - loginfo = msg; - }); - var msg = {payload:"foo", _msgid:"987654321"}; - n.metric("test.metric",msg,"15mb"); - should.deepEqual({value:"15mb", level:Log.METRIC, nodeid:n.id, - event:"node.abc.test.metric",msgid:"987654321"}, loginfo); - Log.log.restore(); - done(); - }); - }); - - describe('#metric', function() { - it('returns metric value if eventname undefined', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var loginfo = {}; - sinon.stub(Log, 'log').callsFake(function(msg) { - loginfo = msg; - }); - var msg = {payload:"foo", _msgid:"987654321"}; - var m = n.metric(undefined,msg,"15mb"); - m.should.be.a.Boolean(); - Log.log.restore(); - done(); - }); - it('returns not defined if eventname defined', function(done) { - var n = new RedNode({id:'123',type:'abc'}); - var loginfo = {}; - sinon.stub(Log, 'log').callsFake(function(msg) { - loginfo = msg; - }); - var msg = {payload:"foo", _msgid:"987654321"}; - var m = n.metric("info",msg,"15mb"); - should(m).be.undefined; - Log.log.restore(); - done(); - }); - }); - - describe('#status', function() { - it('publishes status', function(done) { - var flow = { - handleStatus: sinon.stub() - } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - var status = {fill:"green",shape:"dot",text:"connected"}; - - n.status(status); - - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql(status); - done(); - }); - it('publishes status for plain string', function(done) { - var flow = { handleStatus: sinon.stub() } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - n.status("text status"); - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql({text:"text status"}); - done(); - }); - it('publishes status for plain boolean', function(done) { - var flow = { handleStatus: sinon.stub() } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - n.status(false); - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql({text:"false"}); - done(); - }); - it('publishes status for plain number', function(done) { - var flow = { handleStatus: sinon.stub() } - var n = new RedNode({_flow:flow,id:'123',type:'abc'}); - n.status(123); - flow.handleStatus.called.should.be.true(); - flow.handleStatus.args[0][0].should.eql(n); - flow.handleStatus.args[0][1].should.eql({text:"123"}); - done(); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js deleted file mode 100644 index 7e05eba8f..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js +++ /dev/null @@ -1,1209 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require('sinon'); -var path = require("path"); -var fs = require('fs-extra'); -var NR_TEST_UTILS = require("nr-test-utils"); -var Context = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/context/index"); - -describe('context', function() { - describe('local memory',function() { - beforeEach(function() { - Context.init({}); - Context.load(); - }); - afterEach(function() { - Context.clean({allNodes:{}}); - return Context.close(); - }); - it('stores local property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - should.not.exist(context1.get("foo")); - context1.set("foo","test"); - context1.get("foo").should.equal("test"); - }); - it('stores local property - creates parent properties',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - context1.set("foo.bar","test"); - context1.get("foo").should.eql({bar:"test"}); - }); - it('deletes local property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - context1.set("foo.abc.bar1","test1"); - context1.set("foo.abc.bar2","test2"); - context1.get("foo.abc").should.eql({bar1:"test1",bar2:"test2"}); - context1.set("foo.abc.bar1",undefined); - context1.get("foo.abc").should.eql({bar2:"test2"}); - context1.set("foo.abc",undefined); - should.not.exist(context1.get("foo.abc")); - context1.set("foo",undefined); - should.not.exist(context1.get("foo")); - }); - it('stores flow property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - should.not.exist(context1.flow.get("foo")); - context1.flow.set("foo","test"); - context1.flow.get("foo").should.equal("test"); - }); - it('stores global property',function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - should.not.exist(context1.global.get("foo")); - context1.global.set("foo","test"); - context1.global.get("foo").should.equal("test"); - }); - - it('keeps local context local', function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowA"); - - should.not.exist(context1.get("foo")); - should.not.exist(context2.get("foo")); - context1.set("foo","test"); - - context1.get("foo").should.equal("test"); - should.not.exist(context2.get("foo")); - }); - it('flow context accessible to all flow nodes', function() { - var flowContext = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowA"); - - should.not.exist(context1.flow.get("foo")); - should.not.exist(context2.flow.get("foo")); - - context1.flow.set("foo","test"); - context1.flow.get("foo").should.equal("test"); - context2.flow.get("foo").should.equal("test"); - }); - - it('flow context not shared to nodes on other flows', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB") - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowB"); - - should.not.exist(context1.flow.get("foo")); - should.not.exist(context2.flow.get("foo")); - - context1.flow.set("foo","test"); - context1.flow.get("foo").should.equal("test"); - should.not.exist(context2.flow.get("foo")); - }); - - it('global context shared to all nodes', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB") - - var context1 = Context.get("1","flowA"); - var context2 = Context.get("2","flowB"); - - should.not.exist(context1.global.get("foo")); - should.not.exist(context2.global.get("foo")); - - context1.global.set("foo","test"); - context1.global.get("foo").should.equal("test"); - context2.global.get("foo").should.equal("test"); - }); - - it('context.flow/global are not enumerable', function() { - var flowContextA = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - Object.keys(context1).length.should.equal(0); - Object.keys(context1.flow).length.should.equal(0); - Object.keys(context1.global).length.should.equal(0); - }) - - it('context.flow/global cannot be deleted', function() { - var flowContextA = Context.getFlowContext("flowA") - var context1 = Context.get("1","flowA"); - delete context1.flow; - should.exist(context1.flow); - delete context1.global; - should.exist(context1.global); - }) - - it('deletes context',function() { - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - should.not.exist(context.get("foo")); - context.set("foo","abc"); - context.get("foo").should.equal("abc"); - - return Context.delete("1","flowA").then(function(){ - context = Context.get("1","flowA"); - should.not.exist(context.get("foo")); - }); - }); - - it('enumerates context keys - sync', function() { - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - - var keys = context.keys(); - keys.should.be.an.Array(); - keys.should.be.empty(); - - context.set("foo","bar"); - keys = context.keys(); - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("abc.def","bar"); - keys = context.keys(); - keys.should.have.length(2); - keys[1].should.equal("abc"); - }); - - it('enumerates context keys - async', function(done) { - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - - var keys = context.keys(function(err,keys) { - keys.should.be.an.Array(); - keys.should.be.empty(); - context.set("foo","bar"); - keys = context.keys(function(err,keys) { - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("abc.def","bar"); - keys = context.keys(function(err,keys) { - keys.should.have.length(2); - keys[1].should.equal("abc"); - done(); - }); - }); - }); - }); - - it('should enumerate only context keys when GlobalContext was given - sync', function() { - Context.init({functionGlobalContext: {foo:"bar"}}); - Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - context.global.set("foo2","bar2"); - var keys = context.global.keys(); - keys.should.have.length(2); - keys[0].should.equal("foo"); - keys[1].should.equal("foo2"); - }); - }); - - it('should enumerate only context keys when GlobalContext was given - async', function(done) { - Context.init({functionGlobalContext: {foo:"bar"}}); - Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - context.global.set("foo2","bar2"); - context.global.keys(function(err,keys) { - keys.should.have.length(2); - keys[0].should.equal("foo"); - keys[1].should.equal("foo2"); - done(); - }); - }).catch(done); - }); - - - it('returns functionGlobalContext value if store value undefined', function() { - Context.init({functionGlobalContext: {foo:"bar"}}); - return Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - var v = context.global.get('foo'); - v.should.equal('bar'); - }); - }) - - it('returns functionGlobalContext sub-value if store value undefined', function() { - Context.init({functionGlobalContext: {foo:{bar:123}}}); - return Context.load().then(function(){ - var flowContextA = Context.getFlowContext("flowA") - var context = Context.get("1","flowA"); - var v = context.global.get('foo.bar'); - should.equal(v,123); - }); - }) - - describe("$parent", function() { - it('should get undefined for $parent without key', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - var parent = context1.get("$parent"); - should.equal(parent, undefined); - }); - - it('should get undefined for $parent of root', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - var parent = context1.flow.get("$parent.$parent.K"); - should.equal(parent, undefined); - }); - - it('should get undefined for $parent of root - callback', function(done) { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - context1.flow.get("$parent.$parent.K", function(err, result) { - try { - should.equal(err, undefined); - should.equal(result, undefined); - done(); - } catch(err) { - done(err); - } - }); - - }); - - it('should get value in $parent', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - flowContextA.set("K", "v"); - var v = context1.flow.get("$parent.K"); - should.equal(v, "v"); - }); - - it('should set value in $parent', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - context1.flow.set("$parent.K", "v"); - var v = flowContextA.get("K"); - should.equal(v, "v"); - }); - - it('should not contain $parent in keys', function() { - var flowContextA = Context.getFlowContext("flowA") - var flowContextB = Context.getFlowContext("flowB","flowA") - var context0 = Context.get("0","flowA"); - var context1 = Context.get("1","flowB"); - var parent = context1.get("$parent"); - flowContextA.set("K0", "v0"); - context1.set("K1", "v1"); - var keys = context1.keys(); - keys.should.have.length(1); - keys[0].should.equal("K1"); - }); - }); - - }); - - describe('external context storage',function() { - var resourcesDir = path.resolve(path.join(__dirname,"..","resources","context")); - var sandbox = sinon.createSandbox(); - var stubGet = sandbox.stub(); - var stubSet = sandbox.stub(); - var stubKeys = sandbox.stub(); - var stubDelete = sandbox.stub().returns(Promise.resolve()); - var stubClean = sandbox.stub().returns(Promise.resolve()); - var stubOpen = sandbox.stub().returns(Promise.resolve()); - var stubClose = sandbox.stub().returns(Promise.resolve()); - var stubGet2 = sandbox.stub(); - var stubSet2 = sandbox.stub(); - var stubKeys2 = sandbox.stub(); - var stubDelete2 = sandbox.stub().returns(Promise.resolve()); - var stubClean2 = sandbox.stub().returns(Promise.resolve()); - var stubOpen2 = sandbox.stub().returns(Promise.resolve()); - var stubClose2 = sandbox.stub().returns(Promise.resolve()); - var testPlugin = function(config){ - function Test(){} - Test.prototype.get = stubGet; - Test.prototype.set = stubSet; - Test.prototype.keys = stubKeys; - Test.prototype.delete = stubDelete; - Test.prototype.clean = stubClean; - Test.prototype.open = stubOpen; - Test.prototype.close = stubClose; - return new Test(config); - }; - var testPlugin2 = function(config){ - function Test2(){} - Test2.prototype.get = stubGet2; - Test2.prototype.set = stubSet2; - Test2.prototype.keys = stubKeys2; - Test2.prototype.delete = stubDelete2; - Test2.prototype.clean = stubClean2; - Test2.prototype.open = stubOpen2; - Test2.prototype.close = stubClose2; - return new Test2(config); - }; - var contextStorage={ - test:{ - module: testPlugin, - config:{} - } - }; - var contextDefaultStorage={ - default: { - module: testPlugin2, - config:{} - }, - test:{ - module: testPlugin, - config:{} - } - }; - var contextAlias={ - default: "test", - test:{ - module: testPlugin, - config:{} - } - }; - var memoryStorage ={ - memory:{ - module: "memory" - } - }; - - afterEach(function() { - sandbox.reset(); - return Context.clean({allNodes:{}}).then(function(){ - return Context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - describe('load modules',function(){ - it('should call open()', function() { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - stubOpen.called.should.be.true(); - stubOpen2.called.should.be.true(); - }); - }); - it('should load memory module', function() { - Context.init({contextStorage:{memory:{module:"memory"}}}); - return Context.load(); - }); - it('should load localfilesystem module', function() { - Context.init({contextStorage:{file:{module:"localfilesystem",config:{dir:resourcesDir}}}}); - return Context.load(); - }); - it('should ignore reserved storage name `_`', function(done) { - Context.init({contextStorage:{_:{module:testPlugin}}}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow") - var context = Context.get("1","flow"); - var cb = function(){} - context.set("foo","bar","_",cb); - context.get("foo","_",cb); - context.keys("_",cb); - stubSet.called.should.be.false(); - stubGet.called.should.be.false(); - stubKeys.called.should.be.false(); - done(); - }).catch(done); - }); - - it('should fail when using invalid store name', function(done) { - Context.init({contextStorage:{'Invalid name':{module:testPlugin}}}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail when using invalid sign character', function (done) { - Context.init({ contextStorage:{'abc-123':{module:testPlugin}}}); - Context.load().then(function () { - done("An error was not thrown"); - }).catch(function () { - done(); - }); - }); - it('should fail when using invalid default context', function(done) { - Context.init({contextStorage:{default:"noexist"}}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail for the storage with no module', function(done) { - Context.init({ contextStorage: { test: {}}}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail to load non-existent module', function(done) { - Context.init({contextStorage:{ file:{module:"nonexistent"} }}); - Context.load().then(function(){ - done("An error was not thrown"); - }).catch(function(){ - done(); - }); - }); - it('should fail to load invalid module', function (done) { - Context.init({contextStorage: { - test: { - module: function (config) { - throw new Error("invalid plugin was loaded."); - } - } - }}); - Context.load().then(function () { - done("An error was not thrown"); - }).catch(function () { - done(); - }); - }); - }); - - describe('close modules',function(){ - it('should call close()', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - return Context.close().then(function(){ - stubClose.called.should.be.true(); - stubClose2.called.should.be.true(); - done(); - }); - }).catch(done); - }); - }); - - describe('store context',function() { - it('should store local property to external context storage',function(done) { - Context.init({contextStorage:contextStorage}); - var cb = function(){done("An error occurred")} - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set("foo","bar","test",cb); - context.get("foo","test",cb); - context.keys("test",cb); - stubSet.calledWithExactly("1:flow","foo","bar",cb).should.be.true(); - stubGet.calledWith("1:flow","foo").should.be.true(); - stubKeys.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should store flow property to external context storage',function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.flow.set("foo","bar","test",cb); - context.flow.get("foo","test",cb); - context.flow.keys("test",cb); - stubSet.calledWithExactly("flow","foo","bar",cb).should.be.true(); - stubGet.calledWith("flow","foo").should.be.true(); - stubKeys.calledWithExactly("flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should store global property to external context storage',function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.global.set("foo","bar","test",cb); - context.global.get("foo","test",cb); - context.global.keys("test",cb); - stubSet.calledWithExactly("global","foo","bar",cb).should.be.true(); - stubGet.calledWith("global","foo").should.be.true(); - stubKeys.calledWith("global").should.be.true(); - done(); - }).catch(done); - }); - it('should store data to the default context when non-existent context storage was specified', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","bar","nonexist",cb); - context.get("foo","nonexist",cb); - context.keys("nonexist",cb); - stubGet.called.should.be.false(); - stubSet.called.should.be.false(); - stubKeys.called.should.be.false(); - stubSet2.calledWithExactly("1:flow","foo","bar",cb).should.be.true(); - stubGet2.calledWith("1:flow","foo").should.be.true(); - stubKeys2.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should use the default context', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","bar","default",cb); - context.get("foo","default",cb); - context.keys("default",cb); - stubGet.called.should.be.false(); - stubSet.called.should.be.false(); - stubKeys.called.should.be.false(); - stubSet2.calledWithExactly("1:flow","foo","bar",cb).should.be.true(); - stubGet2.calledWith("1:flow","foo").should.be.true(); - stubKeys2.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should use the alias of default context', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","alias",cb); - context.get("foo",cb); - context.keys(cb); - stubGet.called.should.be.false(); - stubSet.called.should.be.false(); - stubKeys.called.should.be.false(); - stubSet2.calledWithExactly("1:flow","foo","alias",cb).should.be.true(); - stubGet2.calledWith("1:flow","foo").should.be.true(); - stubKeys2.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - - it('should allow the store name to be provide in the key', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("#:(test)::foo","bar"); - context.get("#:(test)::foo"); - stubGet2.called.should.be.false(); - stubSet2.called.should.be.false(); - stubSet.calledWithExactly("1:flow","foo","bar",undefined).should.be.true(); - stubGet.calledWith("1:flow","foo").should.be.true(); - done(); - }).catch(done); - }); - - - it('should use default as the alias of other context', function(done) { - Context.init({contextStorage:contextAlias}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.set("foo","alias",cb); - context.get("foo",cb); - context.keys(cb); - stubSet.calledWithExactly("1:flow","foo","alias",cb).should.be.true(); - stubGet.calledWith("1:flow","foo").should.be.true(); - stubKeys.calledWithExactly("1:flow",cb).should.be.true(); - done(); - }).catch(done); - }); - it('should not throw an error using undefined storage for local context', function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.get("local","nonexist",cb); - done() - }).catch(done); - }); - it('should throw an error using undefined storage for flow context', function(done) { - Context.init({contextStorage:contextStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - var cb = function(){done("An error occurred")} - context.flow.get("flow","nonexist",cb); - done(); - }).catch(done); - }); - - it('should return functionGlobalContext value as a default - synchronous', function(done) { - var fGC = { "foo": 456 }; - Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC }); - Context.load().then(function() { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - // Get foo - should be value from fGC - var v = context.global.get("foo"); - v.should.equal(456); - - // Update foo - should not touch fGC object - context.global.set("foo","new value"); - fGC.foo.should.equal(456); - - // Get foo - should be the updated value - v = context.global.get("foo"); - v.should.equal("new value"); - done(); - }).catch(done); - }) - - it('should return functionGlobalContext value as a default - async', function(done) { - var fGC = { "foo": 456 }; - Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC }); - Context.load().then(function() { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - // Get foo - should be value from fGC - context.global.get("foo", function(err, v) { - if (err) { - done(err) - } else { - v.should.equal(456); - // Update foo - should not touch fGC object - context.global.set("foo","new value", function(err) { - if (err) { - done(err) - } else { - fGC.foo.should.equal(456); - // Get foo - should be the updated value - context.global.get("foo", function(err, v) { - if (err) { - done(err) - } else { - v.should.equal("new value"); - done(); - } - }); - } - }); - } - }); - }).catch(done); - }) - - it('should return multiple values if key is an array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set("foo1","bar1","memory"); - context.set("foo2","bar2","memory"); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - should.not.exist(foo3); - done(); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should return multiple functionGlobalContext values if key is an array', function(done) { - var fGC = { "foo1": 456, "foo2": {"bar":789} }; - Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC }); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.global.get(["foo1","foo2.bar","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - should.equal(foo1, 456); - should.equal(foo2, 789); - should.not.exist(foo3); - done(); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should return an error if an error occurs in getting multiple store values', function(done) { - Context.init({contextStorage:contextStorage}); - stubGet.onFirstCall().callsArgWith(2, "error2", "bar1"); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow") - var context = Context.get("1","flow"); - context.global.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err === "error2") { - done(); - } else { - done("An error occurred"); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should return a first error if some errors occur in getting multiple store values', function(done) { - Context.init({contextStorage:contextStorage}); - stubGet.onFirstCall().callsArgWith(2, "error1"); - stubGet.onSecondCall().callsArgWith(2, null, "bar2"); - stubGet.onThirdCall().callsArgWith(2, "error3"); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err === "error1") { - done(); - } else { - done("An error occurred"); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should store multiple properties if key and value are arrays', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){ - if (err) { - done(err); - } else { - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - foo3.should.be.equal("bar3"); - done(); - } - }); - } - }); - }); - }); - - it('should deletes multiple properties', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){ - if (err) { - done(err); - } else { - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - foo3.should.be.equal("bar3"); - context.set(["foo1","foo2","foo3"], new Array(3), "memory", function(err){ - if (err) { - done(err); - } else { - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - should.not.exist(foo1); - should.not.exist(foo2); - should.not.exist(foo3); - done(); - } - }); - } - }); - } - }); - } - }); - }); - }); - - it('should use null for missing values if the value array is shorter than the key array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2"], "memory", function(err){ - if (err) { - done(err); - } else { - context.keys(function(err, keys){ - keys.should.have.length(3); - keys.should.eql(["foo1","foo2","foo3"]); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - should(foo3).be.null(); - done(); - } - }); - }); - } - }); - }); - }); - - it('should use null for missing values if the value is not array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], "bar1", "memory", function(err){ - if (err) { - done(err); - } else { - context.keys(function(err, keys){ - keys.should.have.length(3); - keys.should.eql(["foo1","foo2","foo3"]); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - should(foo2).be.null(); - should(foo3).be.null(); - done(); - } - }); - }); - } - }); - }); - }); - - it('should ignore the extra values if the value array is longer than the key array', function(done) { - Context.init({contextStorage:memoryStorage}); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3","ignored"], "memory", function(err){ - if (err) { - done(err); - } else { - context.keys(function(err, keys){ - keys.should.have.length(3); - keys.should.eql(["foo1","foo2","foo3"]); - context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){ - if (err) { - done(err); - } else { - foo1.should.be.equal("bar1"); - foo2.should.be.equal("bar2"); - foo3.should.be.equal("bar3"); - done(); - } - }); - }); - } - }); - }); - }); - - it('should return an error if an error occurs in storing multiple values', function(done) { - Context.init({contextStorage:contextStorage}); - stubSet.onFirstCall().callsArgWith(3, "error2"); - Context.load().then(function(){ - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1","flow"); - context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){ - if (err === "error2") { - done(); - } else { - done("An error occurred"); - } - }); - }).catch(function(err){ done(err); }); - }); - - it('should throw an error if callback of context.get is not a function', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.get("foo", "memory", "callback"); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should not throw an error if callback of context.get is not specified', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.get("foo", "memory"); - done(); - }).catch(done); - }); - - it('should throw an error if callback of context.set is not a function', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.set("foo", "bar", "memory", "callback"); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should not throw an error if callback of context.set is not specified', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.set("foo", "bar", "memory"); - done(); - }).catch(done); - }); - - it('should throw an error if callback of context.keys is not a function', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.keys("memory", "callback"); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should not throw an error if callback of context.keys is not specified', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var flowContext = Context.getFlowContext("flow"); - var context = Context.get("1", "flow"); - context.keys("memory"); - done(); - }).catch(done); - }); - it('should throw an error in context.get if key is empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(""); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key is an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get({}); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key is a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key array contains an empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(["ok1", "", "ok2"]); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key array contains an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(["ok1", {}, "ok2"]); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.get if key array contains a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get(["ok1", 1, "ok2"]); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should throw an error in context.set if key is empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set("", 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key is an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set({}, 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key is a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(1, 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key array contains an empty string', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(["ok1", "", "ok2"], 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key array contains an object', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(["ok1", {}, "ok2"], 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - it('should throw an error in context.set if key array contains a number', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set(["ok1", 1, "ok2"], 1); - done("should throw an error."); - }).catch(function () { - done(); - }); - }); - - it('should have an err set in callback for invalid key in context.get', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.get("", function(err) { - if(err) { - done(); - } else { - done("should throw an error."); - } - }); - }).catch(done); - }); - - it('should have an err set in callback for invalid key in context.set', function (done) { - Context.init({ contextStorage: memoryStorage }); - Context.load().then(function () { - var context = Context.get("1", "flow"); - context.set("", "value", function(err) { - if(err) { - done(); - } else { - done("should throw an error."); - } - }); - }).catch(done); - }); - }); - - describe('listStores', function () { - it('should list context storages', function (done) { - Context.init({ contextStorage: contextDefaultStorage }); - Context.load().then(function () { - var list = Context.listStores(); - list.default.should.equal("default"); - list.stores.should.eql(["default", "test"]); - done(); - }).catch(done); - }); - - it('should list context storages without default storage', function (done) { - Context.init({ contextStorage: contextStorage }); - Context.load().then(function () { - var list = Context.listStores(); - list.default.should.equal("test"); - list.stores.should.eql(["test"]); - done(); - }).catch(done); - }); - }); - describe('delete context',function(){ - it('should not call delete() when external context storage is used', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - Context.get("flowA"); - return Context.delete("flowA").then(function(){ - stubDelete.called.should.be.false(); - stubDelete2.called.should.be.false(); - done(); - }); - }).catch(done); - }); - }); - - describe('clean context',function(){ - it('should call clean()', function(done) { - Context.init({contextStorage:contextDefaultStorage}); - Context.load().then(function(){ - return Context.clean({allNodes:{}}).then(function(){ - stubClean.calledWithExactly([]).should.be.true(); - stubClean2.calledWithExactly([]).should.be.true(); - done(); - }); - }).catch(done); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js deleted file mode 100644 index 26f9789f4..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/context/localfilesystem_spec.js +++ /dev/null @@ -1,883 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require('should'); -var fs = require('fs-extra'); -var path = require("path"); -var NR_TEST_UTILS = require("nr-test-utils"); -var LocalFileSystem = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/context/localfilesystem"); - -var resourcesDir = path.resolve(path.join(__dirname,"..","resources","context")); - -var defaultContextBase = "context"; - -describe('localfilesystem',function() { - - before(function() { - return fs.remove(resourcesDir); - }); - - describe('#get/set',function() { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - it('should store property',function(done) { - context.get("nodeX","foo",function(err, value){ - if (err) { return done(err); } - should.not.exist(value); - context.set("nodeX","foo","test",function(err){ - if (err) { return done(err); } - context.get("nodeX","foo",function(err, value){ - if (err) { return done(err); } - value.should.be.equal("test"); - done(); - }); - }); - }); - }); - - it('should store property - creates parent properties',function(done) { - context.set("nodeX","foo.bar","test",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.eql({bar:"test"}); - done(); - }); - }); - }); - - it('should store local scope property', function (done) { - context.set("abc:def", "foo.bar", "test", function (err) { - context.get("abc:def", "foo", function (err, value) { - value.should.be.eql({ bar: "test" }); - done(); - }); - }); - }); - - it('should delete property',function(done) { - context.set("nodeX","foo.abc.bar1","test1",function(err){ - context.set("nodeX","foo.abc.bar2","test2",function(err){ - context.get("nodeX","foo.abc",function(err, value){ - value.should.be.eql({bar1:"test1",bar2:"test2"}); - context.set("nodeX","foo.abc.bar1",undefined,function(err){ - context.get("nodeX","foo.abc",function(err, value){ - value.should.be.eql({bar2:"test2"}); - context.set("nodeX","foo.abc",undefined,function(err){ - context.get("nodeX","foo.abc",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",undefined,function(err){ - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - done(); - }); - }); - }); - }); - }); - }); - }); - }); - }); - }); - - it('should not shared context with other scope', function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.get("nodeY","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","testX",function(err){ - context.set("nodeY","foo","testY",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.equal("testX"); - context.get("nodeY","foo",function(err, value){ - value.should.be.equal("testY"); - done(); - }); - }); - }); - }); - }); - }); - }); - - it('should store string',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","bar",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.String(); - value.should.be.equal("bar"); - context.set("nodeX","foo","1",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.String(); - value.should.be.equal("1"); - done(); - }); - }); - }); - }); - }); - }); - - it('should store number',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",1,function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Number(); - value.should.be.equal(1); - done(); - }); - }); - }); - }); - - it('should store null',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",null,function(err){ - context.get("nodeX","foo",function(err, value){ - should(value).be.null(); - done(); - }); - }); - }); - }); - - it('should store boolean',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",true,function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Boolean().and.true(); - context.set("nodeX","foo",false,function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Boolean().and.false(); - done(); - }); - }); - }); - }); - }); - }); - - it('should store object',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",{obj:"bar"},function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Object(); - value.should.eql({obj:"bar"}); - done(); - }); - }); - }); - }); - - it('should store array',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",["a","b","c"],function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Array(); - value.should.eql(["a","b","c"]); - context.get("nodeX","foo[1]",function(err, value){ - value.should.be.String(); - value.should.equal("b"); - done(); - }); - }); - }); - }); - }); - - it('should store array of arrays',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",[["a","b","c"],[1,2,3,4],[true,false]],function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Array(); - value.should.have.length(3); - value[0].should.have.length(3); - value[1].should.have.length(4); - value[2].should.have.length(2); - context.get("nodeX","foo[1]",function(err, value){ - value.should.be.Array(); - value.should.have.length(4); - value.should.be.eql([1,2,3,4]); - done(); - }); - }); - }); - }); - }); - - it('should store array of objects',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo",[{obj:"bar1"},{obj:"bar2"},{obj:"bar3"}],function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.Array(); - value.should.have.length(3); - value[0].should.be.Object(); - value[1].should.be.Object(); - value[2].should.be.Object(); - context.get("nodeX","foo[1]",function(err, value){ - value.should.be.Object(); - value.should.be.eql({obj:"bar2"}); - done(); - }); - }); - }); - }); - }); - - it('should set/get multiple values', function(done) { - context.set("nodeX",["one","two","three"],["test1","test2","test3"], function(err) { - context.get("nodeX",["one","two"], function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1","test2"]) - done(); - }); - }); - }) - it('should set/get multiple values - get unknown', function(done) { - context.set("nodeX",["one","two","three"],["test1","test2","test3"], function(err) { - context.get("nodeX",["one","two","unknown"], function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1","test2",undefined]) - done(); - }); - }); - }) - it('should set/get multiple values - single value providd', function(done) { - context.set("nodeX",["one","two","three"],"test1", function(err) { - context.get("nodeX",["one","two"], function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1",null]) - done(); - }); - }); - }) - - it('should throw error if bad key included in multiple keys - get', function(done) { - context.set("nodeX",["one","two","three"],["test1","test2","test3"], function(err) { - context.get("nodeX",["one",".foo","three"], function(err) { - should.exist(err); - done(); - }); - }); - }) - - it('should throw error if bad key included in multiple keys - set', function(done) { - context.set("nodeX",["one",".foo","three"],["test1","test2","test3"], function(err) { - should.exist(err); - // Check 'one' didn't get set as a result - context.get("nodeX","one",function(err,one) { - should.not.exist(one); - done(); - }) - }); - }) - - it('should throw an error when getting a value with invalid key', function (done) { - context.set("nodeX","foo","bar",function(err) { - context.get("nodeX"," ",function(err,value) { - should.exist(err); - done(); - }); - }); - }); - - it('should throw an error when setting a value with invalid key',function (done) { - context.set("nodeX"," ","bar",function (err) { - should.exist(err); - done(); - }); - }); - - it('should throw an error when callback of get() is not a function',function (done) { - try { - context.get("nodeX","foo","callback"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should throw an error when callback of get() is not specified',function (done) { - try { - context.get("nodeX","foo"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should throw an error when callback of set() is not a function',function (done) { - try { - context.set("nodeX","foo","bar","callback"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should not throw an error when callback of set() is not specified', function (done) { - try { - context.set("nodeX"," ","bar"); - done(); - } catch (err) { - done("should not throw an error."); - } - }); - - it('should handle empty context file', function (done) { - fs.outputFile(path.join(resourcesDir,defaultContextBase,"nodeX","flow.json"),"",function(){ - context.get("nodeX", "foo", function (err, value) { - should.not.exist(value); - context.set("nodeX", "foo", "test", function (err) { - context.get("nodeX", "foo", function (err, value) { - value.should.be.equal("test"); - done(); - }); - }); - }); - }); - }); - - it('should throw an error when reading corrupt context file', function (done) { - fs.outputFile(path.join(resourcesDir, defaultContextBase, "nodeX", "flow.json"),"{abc",function(){ - context.get("nodeX", "foo", function (err, value) { - should.exist(err); - done(); - }); - }); - }); - }); - - describe('#keys',function() { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - it('should enumerate context keys', function(done) { - context.keys("nodeX",function(err, value){ - value.should.be.an.Array(); - value.should.be.empty(); - context.set("nodeX","foo","bar",function(err){ - context.keys("nodeX",function(err, value){ - value.should.have.length(1); - value[0].should.equal("foo"); - context.set("nodeX","abc.def","bar",function(err){ - context.keys("nodeX",function(err, value){ - value.should.have.length(2); - value[1].should.equal("abc"); - done(); - }); - }); - }); - }); - }); - }); - - it('should enumerate context keys in each scopes', function(done) { - context.keys("nodeX",function(err, value){ - value.should.be.an.Array(); - value.should.be.empty(); - context.keys("nodeY",function(err, value){ - value.should.be.an.Array(); - value.should.be.empty(); - context.set("nodeX","foo","bar",function(err){ - context.set("nodeY","hoge","piyo",function(err){ - context.keys("nodeX",function(err, value){ - value.should.have.length(1); - value[0].should.equal("foo"); - context.keys("nodeY",function(err, value){ - value.should.have.length(1); - value[0].should.equal("hoge"); - done(); - }); - }); - }); - }); - }); - }); - }); - - it('should throw an error when callback of keys() is not a function', function (done) { - try { - context.keys("nodeX", "callback"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - - it('should throw an error when callback of keys() is not specified', function (done) { - try { - context.keys("nodeX"); - done("should throw an error."); - } catch (err) { - done(); - } - }); - }); - - describe('#delete',function() { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - it('should delete context',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.get("nodeY","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","testX",function(err){ - context.set("nodeY","foo","testY",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.be.equal("testX"); - context.get("nodeY","foo",function(err, value){ - value.should.be.equal("testY"); - context.delete("nodeX").then(function(){ - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.get("nodeY","foo",function(err, value){ - value.should.be.equal("testY"); - done(); - }); - }); - }).catch(done); - }); - }); - }); - }); - }); - }); - }); - }); - - describe('#clean',function() { - var context; - var contextGet; - var contextSet; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - contextGet = function(scope,key) { - return new Promise((res,rej) => { - context.get(scope,key, function(err,value) { - if (err) { - rej(err); - } else { - res(value); - } - }) - }); - } - contextSet = function(scope,key,value) { - return new Promise((res,rej) => { - context.set(scope,key,value, function(err) { - if (err) { - rej(err); - } else { - res(); - } - }) - }); - } - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close().then(function(){ - return fs.remove(resourcesDir); - }); - }); - }); - it('should clean unnecessary context',function(done) { - contextSet("global","foo","testGlobal").then(function() { - return contextSet("nodeX:flow1","foo","testX"); - }).then(function() { - return contextSet("nodeY:flow2","foo","testY"); - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - value.should.be.equal("testX"); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - value.should.be.equal("testY"); - }).then(function() { - return context.clean([]) - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - should.not.exist(value); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - should.not.exist(value); - }).then(function() { - return contextGet("global","foo"); - }).then(function(value) { - value.should.eql("testGlobal"); - }).then(done).catch(done); - }); - - it('should not clean active context',function(done) { - contextSet("global","foo","testGlobal").then(function() { - return contextSet("nodeX:flow1","foo","testX"); - }).then(function() { - return contextSet("nodeY:flow2","foo","testY"); - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - value.should.be.equal("testX"); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - value.should.be.equal("testY"); - }).then(function() { - return context.clean(["flow1","nodeX"]) - }).then(function() { - return contextGet("nodeX:flow1","foo"); - }).then(function(value) { - value.should.be.equal("testX"); - }).then(function() { - return contextGet("nodeY:flow2","foo"); - }).then(function(value) { - should.not.exist(value); - }).then(function() { - return contextGet("global","foo"); - }).then(function(value) { - value.should.eql("testGlobal"); - }).then(done).catch(done); - }); - }); - - describe('#if cache is enabled',function() { - - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - - - - it('should load contexts into the cache',function() { - var globalData = {key:"global"}; - var flowData = {key:"flow"}; - var nodeData = {key:"node"}; - return Promise.all([ - fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8"), - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flow","flow.json"), JSON.stringify(flowData,null,4), "utf8"), - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flow","node.json"), JSON.stringify(nodeData,null,4), "utf8") - ]).then(function(){ - context = LocalFileSystem({dir: resourcesDir, cache: true}); - return context.open(); - }).then(function(){ - return Promise.all([ - fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")), - fs.remove(path.join(resourcesDir,defaultContextBase,"flow","flow.json")), - fs.remove(path.join(resourcesDir,defaultContextBase,"flow","node.json")) - ]); - }).then(function(){ - context.get("global","key").should.be.equal("global"); - context.get("flow","key").should.be.equal("flow"); - context.get("node:flow","key").should.be.equal("node"); - }); - }); - - it('should store property to the cache',function() { - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 1}); - return context.open().then(function(){ - return new Promise(function(resolve, reject){ - context.set("global","foo","bar",function(err){ - if(err){ - reject(err); - } else { - fs.readJson(path.join(resourcesDir,defaultContextBase,"global","global.json")).then(function(data) { - // File should not exist as flush hasn't happened - reject("File global/global.json should not exist"); - }).catch(function(err) { - setTimeout(function() { - fs.readJson(path.join(resourcesDir,defaultContextBase,"global","global.json")).then(function(data) { - data.should.eql({foo:'bar'}); - resolve(); - }).catch(function(err) { - reject(err); - }); - },1100) - }) - } - }); - }); - }).then(function(){ - return fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")); - }).then(function(){ - context.get("global","foo").should.be.equal("bar"); - }) - }); - - it('should enumerate context keys in the cache',function() { - var globalData = {foo:"bar"}; - return fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8").then(function(){ - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2}); - return context.open() - }).then(function(){ - return fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")); - }).then(function(){ - var keys = context.keys("global"); - keys.should.have.length(1); - keys[0].should.equal("foo"); - return new Promise(function(resolve, reject){ - context.set("global","foo2","bar2",function(err){ - if(err){ - reject(err); - } else { - resolve(); - } - }); - }); - }).then(function(){ - return fs.remove(path.join(resourcesDir,defaultContextBase,"global","global.json")); - }).then(function(){ - var keys = context.keys("global"); - keys.should.have.length(2); - keys[1].should.equal("foo2"); - }) - }); - - it('should delete context in the cache',function() { - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2}); - return context.open().then(function(){ - return new Promise(function(resolve, reject){ - context.set("global","foo","bar",function(err){ - if(err){ - reject(err); - } else { - resolve(); - } - }); - }); - }).then(function(){ - context.get("global","foo").should.be.equal("bar"); - return context.delete("global"); - }).then(function(){ - should.not.exist(context.get("global","foo")) - }) - }); - - it('should clean unnecessary context in the cache',function() { - var flowAData = {key:"flowA"}; - var flowBData = {key:"flowB"}; - return Promise.all([ - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flowA","flow.json"), JSON.stringify(flowAData,null,4), "utf8"), - fs.outputFile(path.join(resourcesDir,defaultContextBase,"flowB","flow.json"), JSON.stringify(flowBData,null,4), "utf8") - ]).then(function(){ - context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2}); - return context.open(); - }).then(function(){ - context.get("flowA","key").should.be.equal("flowA"); - context.get("flowB","key").should.be.equal("flowB"); - return context.clean(["flowA"]); - }).then(function(){ - context.get("flowA","key").should.be.equal("flowA"); - should.not.exist(context.get("flowB","key")); - }); - }); - }); - - describe('Configuration', function () { - var context; - beforeEach(function() { - context = LocalFileSystem({dir: resourcesDir, cache: false}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }).then(function(){ - return fs.remove(resourcesDir); - }); - }); - it('should change a base directory', function (done) { - var differentBaseContext = LocalFileSystem({ - base: "contexts2", - dir: resourcesDir, - cache: false - }); - differentBaseContext.open().then(function () { - differentBaseContext.set("node2", "foo2", "bar2", function (err) { - differentBaseContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function(err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - }); - - it('should use userDir', function (done) { - var userDirContext = LocalFileSystem({ - base: "contexts2", - cache: false, - settings: { - userDir: resourcesDir - } - }); - userDirContext.open().then(function () { - userDirContext.set("node2", "foo2", "bar2", function (err) { - userDirContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - }); - - it('should use NODE_RED_HOME', function (done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = resourcesDir; - fs.ensureDirSync(resourcesDir); - fs.writeFileSync(path.join(resourcesDir,".config.json"),""); - var nrHomeContext = LocalFileSystem({ - base: "contexts2", - cache: false - }); - try { - nrHomeContext.open().then(function () { - nrHomeContext.set("node2", "foo2", "bar2", function (err) { - nrHomeContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - } finally { - process.env.NODE_RED_HOME = oldNRH; - } - }); - - it('should use HOME_PATH', function (done) { - var oldNRH = process.env.NODE_RED_HOME; - var oldHOMEPATH = process.env.HOMEPATH; - process.env.NODE_RED_HOME = resourcesDir; - process.env.HOMEPATH = resourcesDir; - var homePath = path.join(resourcesDir, ".node-red"); - fs.outputFile(path.join(homePath, ".config.json"),"",function(){ - var homeContext = LocalFileSystem({ - base: "contexts2", - cache: false - }); - try { - homeContext.open().then(function () { - homeContext.set("node2", "foo2", "bar2", function (err) { - homeContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOMEPATH = oldHOMEPATH; - } - }); - }); - - it('should use HOME_PATH', function (done) { - var oldNRH = process.env.NODE_RED_HOME; - var oldHOMEPATH = process.env.HOMEPATH; - var oldHOME = process.env.HOME; - process.env.NODE_RED_HOME = resourcesDir; - process.env.HOMEPATH = resourcesDir; - process.env.HOME = resourcesDir; - var homeContext = LocalFileSystem({ - base: "contexts2", - cache: false - }); - try { - homeContext.open().then(function () { - homeContext.set("node2", "foo2", "bar2", function (err) { - homeContext.get("node2", "foo2", function (err, value) { - value.should.be.equal("bar2"); - context.get("node2", "foo2", function (err, value) { - should.not.exist(value); - done(); - }); - }); - }); - }); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOMEPATH = oldHOMEPATH; - process.env.HOME = oldHOME; - } - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js deleted file mode 100644 index 663ee46b7..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/context/memory_spec.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require('should'); -var NR_TEST_UTILS = require("nr-test-utils"); - -var Memory = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/context/memory"); - -describe('memory',function() { - var context; - - beforeEach(function() { - context = Memory({}); - return context.open(); - }); - - afterEach(function() { - return context.clean([]).then(function(){ - return context.close(); - }); - }); - - describe('#get/set',function() { - describe('sync',function() { - it('should store property',function() { - should.not.exist(context.get("nodeX","foo")); - context.set("nodeX","foo","test"); - context.get("nodeX","foo").should.equal("test"); - }); - - it('should store property - creates parent properties',function() { - context.set("nodeX","foo.bar","test"); - context.get("nodeX","foo").should.eql({bar:"test"}); - }); - - it('should delete property',function() { - context.set("nodeX","foo.abc.bar1","test1"); - context.set("nodeX","foo.abc.bar2","test2"); - context.get("nodeX","foo.abc").should.eql({bar1:"test1",bar2:"test2"}); - context.set("nodeX","foo.abc.bar1",undefined); - context.get("nodeX","foo.abc").should.eql({bar2:"test2"}); - context.set("nodeX","foo.abc",undefined); - should.not.exist(context.get("nodeX","foo.abc")); - context.set("nodeX","foo",undefined); - should.not.exist(context.get("nodeX","foo")); - }); - - it('should not shared context with other scope', function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","testX"); - context.set("nodeY","foo","testY"); - - context.get("nodeX","foo").should.equal("testX"); - context.get("nodeY","foo").should.equal("testY"); - }); - - it('should throw the error if the error occurs', function() { - try{ - context.set("nodeX",".foo","test"); - should.fail("Error was not thrown"); - }catch(err){ - should.exist(err); - try{ - context.get("nodeX",".foo"); - should.fail("Error was not thrown"); - }catch(err){ - should.exist(err); - } - } - }); - - it('should get multiple values - all known', function() { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - var values = context.get("nodeX",["one","two","four"]); - values.should.eql(["test1","test2","test4"]) - }) - it('should get multiple values - include unknown', function() { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - var values = context.get("nodeX",["one","unknown.with.multiple.levels"]); - values.should.eql(["test1",undefined]) - }) - it('should throw error if bad key included in multiple keys', function() { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - try{ - var values = context.get("nodeX",["one",".foo","three"]); - should.fail("Error was not thrown"); - }catch(err){ - should.exist(err); - } - }) - - - }); - - describe('async',function() { - it('should store property',function(done) { - context.get("nodeX","foo",function(err, value){ - should.not.exist(value); - context.set("nodeX","foo","test",function(err){ - context.get("nodeX","foo",function(err, value){ - value.should.equal("test"); - done(); - }); - }); - }); - }); - - it('should pass the error to callback if the error occurs',function(done) { - context.set("nodeX",".foo","test",function(err, value){ - should.exist(err); - context.get("nodeX",".foo",function(err){ - should.exist(err); - done(); - }); - }); - }); - - it('should get multiple values - all known', function(done) { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - context.get("nodeX",["one","two","four"],function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1","test2","test4"]) - done(); - }); - }) - it('should get multiple values - include unknown', function(done) { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - context.get("nodeX",["one","unknown"],function() { - Array.prototype.slice.apply(arguments).should.eql([undefined,"test1",undefined]) - done(); - }); - }) - it('should throw error if bad key included in multiple keys', function(done) { - context.set("nodeX","one","test1"); - context.set("nodeX","two","test2"); - context.set("nodeX","three","test3"); - context.set("nodeX","four","test4"); - - context.get("nodeX",["one",".foo","three"], function(err) { - should.exist(err); - done(); - }); - }) - }); - }); - - describe('#keys',function() { - describe('sync',function() { - it('should enumerate context keys', function() { - var keys = context.keys("nodeX"); - keys.should.be.an.Array(); - keys.should.be.empty(); - - context.set("nodeX","foo","bar"); - keys = context.keys("nodeX"); - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("nodeX","abc.def","bar"); - keys = context.keys("nodeX"); - keys.should.have.length(2); - keys[1].should.equal("abc"); - }); - - it('should enumerate context keys in each scopes', function() { - var keysX = context.keys("nodeX"); - keysX.should.be.an.Array(); - keysX.should.be.empty(); - - var keysY = context.keys("nodeY"); - keysY.should.be.an.Array(); - keysY.should.be.empty(); - - context.set("nodeX","foo","bar"); - context.set("nodeY","hoge","piyo"); - keysX = context.keys("nodeX"); - keysX.should.have.length(1); - keysX[0].should.equal("foo"); - - keysY = context.keys("nodeY"); - keysY.should.have.length(1); - keysY[0].should.equal("hoge"); - }); - - it('should enumerate global context keys', function () { - var keys = context.keys("global"); - keys.should.be.an.Array(); - keys.should.be.empty(); - - context.set("global", "foo", "bar"); - keys = context.keys("global"); - keys.should.have.length(1); - keys[0].should.equal("foo"); - - context.set("global", "abc.def", "bar"); - keys = context.keys("global"); - keys.should.have.length(2); - keys[1].should.equal("abc"); - }); - - it('should not return specific keys as global context keys', function () { - var keys = context.keys("global"); - - context.set("global", "set", "bar"); - context.set("global", "get", "bar"); - context.set("global", "keys", "bar"); - keys = context.keys("global"); - keys.should.have.length(0); - }); - }); - - describe('async',function() { - it('should enumerate context keys', function(done) { - context.keys("nodeX", function(err, keys) { - keys.should.be.an.Array(); - keys.should.be.empty(); - context.set("nodeX", "foo", "bar", function(err) { - context.keys("nodeX", function(err, keys) { - keys.should.have.length(1); - keys[0].should.equal("foo"); - context.set("nodeX","abc.def","bar",function(err){ - context.keys("nodeX",function(err, keys){ - keys.should.have.length(2); - keys[1].should.equal("abc"); - done(); - }); - }); - }); - }); - }); - }); - }); - }); - - describe('#delete',function() { - it('should delete context',function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","abc"); - context.set("nodeY","foo","abc"); - context.get("nodeX","foo").should.equal("abc"); - context.get("nodeY","foo").should.equal("abc"); - - return context.delete("nodeX").then(function(){ - should.not.exist(context.get("nodeX","foo")); - should.exist(context.get("nodeY","foo")); - }); - }); - }); - - describe('#clean',function() { - it('should clean unnecessary context',function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","abc"); - context.set("nodeY","foo","abc"); - context.get("nodeX","foo").should.equal("abc"); - context.get("nodeY","foo").should.equal("abc"); - - return context.clean([]).then(function(){ - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - }); - }); - it('should not clean active context',function() { - should.not.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - context.set("nodeX","foo","abc"); - context.set("nodeY","foo","abc"); - context.get("nodeX","foo").should.equal("abc"); - context.get("nodeY","foo").should.equal("abc"); - - return context.clean(["nodeX"]).then(function(){ - should.exist(context.get("nodeX","foo")); - should.not.exist(context.get("nodeY","foo")); - }); - }); - it('should not clean global context', function () { - context.set("global", "foo", "abc"); - context.get("global", "foo").should.equal("abc"); - - return context.clean(["global"]).then(function () { - should.exist(context.get("global", "foo")); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/credentials_spec.js b/test/unit/@node-red/runtime/lib/nodes/credentials_spec.js deleted file mode 100644 index 6db0b867b..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/credentials_spec.js +++ /dev/null @@ -1,474 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var index = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/index"); -var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); -var log = NR_TEST_UTILS.require("@node-red/util").log; - - -describe('red/runtime/nodes/credentials', function() { - - var encryptionDisabledSettings = { - get: function(key) { - return false; - } - } - - afterEach(function() { - index.clearRegistry(); - }); - - it('loads provided credentials',function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - - return credentials.load({"a":{"b":1,"c":2}}).then(function() { - credentials.get("a").should.have.property('b',1); - credentials.get("a").should.have.property('c',2); - }); - }); - it('adds a new credential',function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - return credentials.load({"a":{"b":1,"c":2}}).then(function() { - credentials.dirty().should.be.false(); - should.not.exist(credentials.get("b")); - return credentials.add("b",{"foo":"bar"}).then(function() { - credentials.get("b").should.have.property("foo","bar"); - credentials.dirty().should.be.true(); - }); - }); - }); - it('deletes an existing credential',function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - return credentials.load({"a":{"b":1,"c":2}}).then(function() { - credentials.dirty().should.be.false(); - credentials.delete("a"); - should.not.exist(credentials.get("a")); - credentials.dirty().should.be.true(); - }); - }); - - it('exports the credentials, clearing dirty flag', function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings - }); - var creds = {"a":{"b":1,"c":2}}; - return credentials.load(creds).then(function() { - return credentials.add("b",{"foo":"bar"}) - }).then(function() { - credentials.dirty().should.be.true(); - return credentials.export().then(function(exported) { - exported.should.eql(creds); - credentials.dirty().should.be.false(); - }) - }); - }) - - describe("#clean",function() { - it("removes credentials of unknown nodes",function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - var creds = {"a":{"b":1,"c":2},"b":{"d":3}}; - return credentials.load(creds).then(function() { - credentials.dirty().should.be.false(); - should.exist(credentials.get("a")); - should.exist(credentials.get("b")); - return credentials.clean([{id:"b"}]).then(function() { - credentials.dirty().should.be.true(); - should.not.exist(credentials.get("a")); - should.exist(credentials.get("b")); - }); - }); - }); - it("extracts credentials of known nodes",function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - credentials.register("testNode",{"b":"text","c":"password"}) - var creds = {"a":{"b":1,"c":2}}; - var newConfig = [{id:"a",type:"testNode",credentials:{"b":"newBValue","c":"newCValue"}}]; - return credentials.load(creds).then(function() { - credentials.dirty().should.be.false(); - return credentials.clean(newConfig).then(function() { - credentials.dirty().should.be.true(); - credentials.get("a").should.have.property('b',"newBValue"); - credentials.get("a").should.have.property('c',"newCValue"); - should.not.exist(newConfig[0].credentials); - }); - }); - }); - - - }); - - it('warns if a node has no credential definition', function() { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - return credentials.load({}).then(function() { - var node = {id:"node",type:"test",credentials:{ - user1:"newUser", - password1:"newPassword" - }}; - sinon.spy(log,"warn"); - credentials.extract(node); - log.warn.called.should.be.true(); - should.not.exist(node.credentials); - log.warn.restore(); - }); - }) - - it('extract credential updates in the provided node', function(done) { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - var defintion = { - user1:{type:"text"}, - password1:{type:"password"}, - user2:{type:"text"}, - password2:{type:"password"}, - user3:{type:"text"}, - password3:{type:"password"} - - }; - credentials.register("test",defintion); - var def = credentials.getDefinition("test"); - defintion.should.eql(def); - - credentials.load({"node":{user1:"abc",password1:"123",user2:"def",password2:"456",user3:"ghi",password3:"789"}}).then(function() { - var node = {id:"node",type:"test",credentials:{ - // user1 unchanged - password1:"__PWRD__", - user2: "", - password2:" ", - user3:"newUser", - password3:"newPassword" - }}; - credentials.dirty().should.be.false(); - credentials.extract(node); - - node.should.not.have.a.property("credentials"); - - credentials.dirty().should.be.true(); - var newCreds = credentials.get("node"); - newCreds.should.have.a.property("user1","abc"); - newCreds.should.have.a.property("password1","123"); - newCreds.should.not.have.a.property("user2"); - newCreds.should.not.have.a.property("password2"); - newCreds.should.have.a.property("user3","newUser"); - newCreds.should.have.a.property("password3","newPassword"); - - done(); - }); - }); - it('extract ignores node without credentials', function(done) { - credentials.init({ - log: log, - settings: encryptionDisabledSettings, - nodes: { getType: () => function(){} } - }); - credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() { - var node = {id:"node",type:"test"}; - - credentials.dirty().should.be.false(); - credentials.extract(node); - credentials.dirty().should.be.false(); - done(); - }); - }); - - describe("encryption",function() { - var settings = {}; - var runtime = { - log: log, - settings: { - get: function(key) { - return settings[key]; - }, - set: function(key,value) { - settings[key] = value; - return Promise.resolve(); - }, - delete: function(key) { - delete settings[key]; - return Promise.resolve(); - } - }, - nodes: { getType: () => function(){} } - } - it('migrates to encrypted and generates default key', function(done) { - settings = {}; - credentials.init(runtime); - credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() { - settings.should.have.a.property("_credentialSecret"); - settings._credentialSecret.should.have.a.length(64); - credentials.dirty().should.be.true(); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - done(); - }) - }); - }); - }); - it('uses default key', function(done) { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - should.exist(credentials.get("node")); - credentials.dirty().should.be.false(); - credentials.add("node",{user1:"def",password1:"456"}); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","def"); - credentials.get("node").should.have.a.property("password1","456"); - done(); - }) - }); - }); - }); - it('uses user key', function(done) { - settings = { - credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - credentials.dirty().should.be.false(); - should.exist(credentials.get("node")); - credentials.add("node",{user1:"def",password1:"456"}); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","def"); - credentials.get("node").should.have.a.property("password1","456"); - done(); - }) - }); - }); - }); - it('uses user key - when settings are otherwise unavailable', function(done) { - var runtime = { - log: log, - settings: { - get: function(key) { - if (key === 'credentialSecret') { - return "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a"; - } - throw new Error(); - }, - set: function(key,value) { - throw new Error(); - } - } - } - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - should.exist(credentials.get("node")); - credentials.add("node",{user1:"def",password1:"456"}); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","def"); - credentials.get("node").should.have.a.property("password1","456"); - done(); - }) - }); - }); - }); - it('migrates from default key to user key', function() { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a", - credentialSecret: "aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbcccccccccccccddddddddddddeeeee" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - return credentials.load(cryptedFlows).then(function() { - credentials.dirty().should.be.true(); - should.exist(credentials.get("node")); - return credentials.export().then(function(result) { - result.should.have.a.property("$"); - settings.should.not.have.a.property("_credentialSecret"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - return credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","abc"); - credentials.get("node").should.have.a.property("password1","123"); - }) - }); - }); - }); - - it('migrates from default key to user key - unencrypted original', function(done) { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a", - credentialSecret: "aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbcccccccccccccddddddddddddeeeee" - }; - // {"node":{user1:"abc",password1:"123"}} - var unencryptedFlows = {"node":{user1:"abc",password1:"123"}}; - credentials.init(runtime); - credentials.load(unencryptedFlows).then(function() { - credentials.dirty().should.be.true(); - should.exist(credentials.get("node")); - credentials.export().then(function(result) { - result.should.have.a.property("$"); - settings.should.not.have.a.property("_credentialSecret"); - - // reset everything - but with _credentialSecret still set - credentials.init(runtime); - // load the freshly encrypted version - credentials.load(result).then(function() { - should.exist(credentials.get("node")); - credentials.get("node").should.have.a.property("user1","abc"); - credentials.get("node").should.have.a.property("password1","123"); - done(); - }) - }); - }); - }); - - it('migrates from default key to unencrypted', function(done) { - settings = { - _credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a", - credentialSecret: false - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - credentials.dirty().should.be.true(); - should.exist(credentials.get("node")); - credentials.export().then(function(result) { - result.should.not.have.a.property("$"); - settings.should.not.have.a.property("_credentialSecret"); - result.should.eql({"node":{user1:"abc",password1:"123"}}); - done(); - }); - }); - }); - it('handles bad default key - resets credentials', function(done) { - settings = { - _credentialSecret: "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - // credentials.dirty().should.be.true(); - // should.not.exist(credentials.get("node")); - done(); - }).catch(function(err) { - err.should.have.property('code','credentials_load_failed'); - done(); - }); - }); - it('handles bad user key - resets credentials', function(done) { - settings = { - credentialSecret: "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb" - }; - // {"node":{user1:"abc",password1:"123"}} - var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"}; - credentials.init(runtime); - credentials.load(cryptedFlows).then(function() { - // credentials.dirty().should.be.true(); - // should.not.exist(credentials.get("node")); - done(); - }).catch(function(err) { - err.should.have.property('code','credentials_load_failed'); - done(); - }); - }); - - it('handles unavailable settings - leaves creds unencrypted', function(done) { - var runtime = { - log: log, - settings: { - get: function(key) { - throw new Error(); - }, - set: function(key,value) { - throw new Error(); - } - }, - nodes: { getType: () => function(){} } - } - // {"node":{user1:"abc",password1:"123"}} - credentials.init(runtime); - credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() { - credentials.dirty().should.be.false(); - should.exist(credentials.get("node")); - credentials.export().then(function(result) { - result.should.not.have.a.property("$"); - result.should.have.a.property("node"); - done(); - }); - }); - }); - }) -}) diff --git a/test/unit/@node-red/runtime/lib/nodes/index_spec.js b/test/unit/@node-red/runtime/lib/nodes/index_spec.js deleted file mode 100644 index d6017db45..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/index_spec.js +++ /dev/null @@ -1,404 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); -var sinon = require('sinon'); -var inherits = require("util").inherits; - -var NR_TEST_UTILS = require("nr-test-utils"); -var index = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/index"); -var flows = NR_TEST_UTILS.require("@node-red/runtime/lib/flows"); -var registry = NR_TEST_UTILS.require("@node-red/registry") -var Node = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/Node"); - -describe("red/nodes/index", function() { - before(function() { - sinon.stub(index,"startFlows"); - process.env.NODE_RED_HOME = NR_TEST_UTILS.resolve("node-red"); - process.env.foo="bar"; - }); - after(function() { - index.startFlows.restore(); - delete process.env.NODE_RED_HOME; - delete process.env.foo; - }); - - afterEach(function() { - index.clearRegistry(); - }); - - var testFlows = [{"type":"test","id":"tab1","label":"Sheet 1"}]; - var testCredentials = {"tab1":{"b":1, "c":"2", "d":"$(foo)"}}; - var storage = { - getFlows: function() { - return Promise.resolve({red:123,flows:testFlows,credentials:testCredentials}); - }, - saveFlows: function(conf) { - should.deepEqual(testFlows, conf.flows); - return Promise.resolve(123); - } - }; - - var settings = { - available: function() { return false }, - get: function() { return false } - }; - - var EventEmitter = require('events').EventEmitter; - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:function() {}}, - events: new EventEmitter() - }; - - function TestNode(n) { - this._flow = {getSetting: p => process.env[p]}; - index.createNode(this, n); - this.on("log", function() { - // do nothing - }); - } - - it('nodes are initialised with credentials',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - var testnode = new TestNode({id:'tab1',type:'test',name:'barney'}); - testnode.credentials.should.have.property('b',1); - testnode.credentials.should.have.property('c',"2"); - testnode.credentials.should.have.property('d',"bar"); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('flows should be initialised',function(done) { - index.init(runtime); - index.loadFlows().then(function() { - // console.log(testFlows); - // console.log(index.getFlows()); - should.deepEqual(testFlows, index.getFlows().flows); - done(); - }).catch(function(err) { - done(err); - }); - - }); - describe("registerType", function() { - describe("logs deprecated usage", function() { - before(function() { - sinon.stub(registry,"registerType"); - }); - after(function() { - registry.registerType.restore(); - }); - it("called without node-set name", function() { - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:sinon.spy()}, - events: new EventEmitter() - } - index.init(runtime); - - index.registerType(/*'test-node-set',*/'test', TestNode, {}); - runtime.log.warn.called.should.be.true(); - registry.registerType.called.should.be.true(); - registry.registerType.firstCall.args[0].should.eql(''); - registry.registerType.firstCall.args[1].should.eql('test'); - registry.registerType.firstCall.args[2].should.eql(TestNode); - }); - }); - describe("extends constructor with Node constructor", function() { - var TestNodeConstructor; - before(function() { - sinon.stub(registry,"registerType"); - }); - after(function() { - registry.registerType.restore(); - }); - beforeEach(function() { - TestNodeConstructor = function TestNodeConstructor() {}; - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:sinon.spy()}, - events: new EventEmitter() - } - index.init(runtime); - }) - it('extends a constructor with the Node constructor', function() { - TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node); - index.registerType('node-set','node-type',TestNodeConstructor); - TestNodeConstructor.prototype.should.be.an.instanceOf(Node); - }); - it('does not override a constructor prototype', function() { - function Foo(){}; - inherits(TestNodeConstructor,Foo); - TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); - TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node); - - index.registerType('node-set','node-type',TestNodeConstructor); - - TestNodeConstructor.prototype.should.be.an.instanceOf(Node); - TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); - - index.registerType('node-set','node-type2',TestNodeConstructor); - TestNodeConstructor.prototype.should.be.an.instanceOf(Node); - TestNodeConstructor.prototype.should.be.an.instanceOf(Foo); - }); - }); - describe("register credentials definition", function() { - var http = require('http'); - var express = require('express'); - var app = express(); - var runtime = NR_TEST_UTILS.require("@node-red/runtime"); - var credentials = NR_TEST_UTILS.require("@node-red/runtime/lib/nodes/credentials"); - var localfilesystem = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem"); - var log = NR_TEST_UTILS.require("@node-red/util").log; - var RED = NR_TEST_UTILS.require("node-red/lib/red.js"); - - var userDir = path.join(__dirname,".testUserHome"); - before(function(done) { - sinon.stub(log,"log").callsFake(function(){}); - fs.remove(userDir,function(err) { - fs.mkdir(userDir,function() { - sinon.stub(index, 'load').callsFake(function() { - return new Promise(function(resolve,reject){ - resolve([]); - }); - }); - sinon.stub(localfilesystem, 'getCredentials').callsFake(function() { - return new Promise(function(resolve,reject) { - resolve({"tab1":{"b":1,"c":2}}); - }); - }) ; - RED.init(http.createServer(function(req,res){app(req,res)}), - {userDir: userDir}); - runtime.start().then(function () { - done(); - }); - }); - }); - }); - - after(function(done) { - fs.remove(userDir,function() { - runtime.stop().then(function() { - index.load.restore(); - localfilesystem.getCredentials.restore(); - log.log.restore(); - done(); - }); - }); - }); - - it('definition defined',function() { - index.registerType('test-node-set','test', TestNode, { - credentials: { - foo: {type:"test"} - } - }); - var testnode = new TestNode({id:'tab1',type:'test',name:'barney', '_alias':'tab1'}); - index.getCredentialDefinition("test").should.have.property('foo'); - }); - }); - - describe("register settings definition", function() { - beforeEach(function() { - sinon.stub(registry,"registerType"); - }) - afterEach(function() { - registry.registerType.restore(); - }) - it('registers valid settings',function() { - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:function() {}}, - events: new EventEmitter() - } - runtime.settings.registerNodeSettings = sinon.spy(); - index.init(runtime); - - index.registerType('test-node-set','test', TestNode, { - settings: { - testOne: {} - } - }); - runtime.settings.registerNodeSettings.called.should.be.true(); - runtime.settings.registerNodeSettings.firstCall.args[0].should.eql('test'); - runtime.settings.registerNodeSettings.firstCall.args[1].should.eql({testOne: {}}); - }); - it('logs invalid settings',function() { - var runtime = { - settings: settings, - storage: storage, - log: {debug:function() {}, warn:sinon.spy()}, - events: new EventEmitter() - } - runtime.settings.registerNodeSettings = function() { throw new Error("pass");} - index.init(runtime); - - index.registerType('test-node-set','test', TestNode, { - settings: { - testOne: {} - } - }); - runtime.log.warn.called.should.be.true(); - }); - }); - }); - - describe('allows nodes to be added/removed/enabled/disabled from the registry', function() { - var randomNodeInfo = {id:"5678",types:["random"]}; - - beforeEach(function() { - sinon.stub(registry,"getNodeInfo").callsFake(function(id) { - if (id == "test") { - return {id:"1234",types:["test"]}; - } else if (id == "doesnotexist") { - return null; - } else { - return randomNodeInfo; - } - }); - sinon.stub(registry,"disableNode").callsFake(function(id) { - return Promise.resolve(randomNodeInfo); - }); - }); - afterEach(function() { - registry.getNodeInfo.restore(); - registry.disableNode.restore(); - }); - - it('allows an unused node type to be disabled',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - return index.disableNode("5678").then(function(info) { - registry.disableNode.calledOnce.should.be.true(); - registry.disableNode.calledWith("5678").should.be.true(); - info.should.eql(randomNodeInfo); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('prevents disabling a node type that is in use',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.disabledNode("test"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('prevents disabling a node type that is unknown',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.disableNode("doesnotexist"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); - - describe('allows modules to be removed from the registry', function() { - var randomNodeInfo = {id:"5678",types:["random"]}; - var randomModuleInfo = { - name:"random", - nodes: [randomNodeInfo] - }; - - before(function() { - sinon.stub(registry,"getNodeInfo").callsFake(function(id) { - if (id == "node-red/foo") { - return {id:"1234",types:["test"]}; - } else if (id == "doesnotexist") { - return null; - } else { - return randomNodeInfo; - } - }); - sinon.stub(registry,"getModuleInfo").callsFake(function(module) { - if (module == "node-red") { - return {nodes:[{name:"foo"}]}; - } else if (module == "doesnotexist") { - return null; - } else { - return randomModuleInfo; - } - }); - sinon.stub(registry,"removeModule").callsFake(function(id) { - return randomModuleInfo; - }); - }); - after(function() { - registry.getNodeInfo.restore(); - registry.getModuleInfo.restore(); - registry.removeModule.restore(); - }); - - it('prevents removing a module that is in use',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.removeModule("node-red"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('prevents removing a module that is unknown',function(done) { - index.init(runtime); - index.registerType('test-node-set','test', TestNode); - index.loadFlows().then(function() { - /*jshint immed: false */ - (function() { - index.removeModule("doesnotexist"); - }).should.throw(); - - done(); - }).catch(function(err) { - done(err); - }); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png b/test/unit/@node-red/runtime/lib/nodes/resources/local/NestedDirectoryNode/NestedNode/icons/arrow-in.png deleted file mode 100644 index e38f3914600901b736f5fa18786ee11be6d41c28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6icy_X9x!n)NrJ90QsB+9+AZi z3~X;em{G3O!W1YdS>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjT8d|Bb%p- zV~9uR+N-+$hZ97Oeyp#Y$RU!&XX#m>@|$agvYL9Jz$O6}72y^gZVi>ZMeaH^t*Q|! zLhVk<0xdko8zYVo#8M-LBBd8b>1kw|i?FO)bWqsy_rwQg27N4y zeX6pqO$_Cex^^A7_NsS@+=bOASA^wN0&GKN(hlAguRU&q- jTBY?`*L>xN+|S&+7B*edvmQhOgN?z{)z4*}Q$iB}+9!`i diff --git a/test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png b/test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png deleted file mode 100644 index 59a29af14..000000000 --- a/test/unit/@node-red/runtime/lib/nodes/resources/local/TestNodeModule/node_modules/TestNodeModule/icons/arrow-in.png +++ /dev/null @@ -1,3 +0,0 @@ -This file exists just to ensure the 'icons' directory is in the repository. -TODO: a future test needs to ensure the right icon files are loaded - this - directory can be used for that diff --git a/test/unit/@node-red/runtime/lib/plugins_spec.js b/test/unit/@node-red/runtime/lib/plugins_spec.js deleted file mode 100644 index a78de643c..000000000 --- a/test/unit/@node-red/runtime/lib/plugins_spec.js +++ /dev/null @@ -1,13 +0,0 @@ -const should = require("should"); -const sinon = require("sinon"); -const NR_TEST_UTILS = require("nr-test-utils"); - -const plugins = NR_TEST_UTILS.require("@node-red/runtime/lib/plugins"); - -describe("runtime/plugins",function() { - - it.skip("delegates all functions to registry module", function() { - // There's no easy way to test this as we can't stub the registry functions - // before the plugin module gets a reference to them - }) -}); diff --git a/test/unit/@node-red/runtime/lib/settings_spec.js b/test/unit/@node-red/runtime/lib/settings_spec.js deleted file mode 100644 index 51c190fea..000000000 --- a/test/unit/@node-red/runtime/lib/settings_spec.js +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var settings = NR_TEST_UTILS.require("@node-red/runtime/lib/settings"); - - -describe("runtime/settings", function() { - - afterEach(function() { - settings.reset(); - }); - - it('wraps the user settings as read-only properties', function() { - var userSettings = { - a: 123, - b: "test", - c: [1,2,3] - } - settings.init(userSettings); - - settings.available().should.be.false(); - - settings.a.should.equal(123); - settings.b.should.equal("test"); - settings.c.should.be.an.Array(); - settings.c.should.have.lengthOf(3); - - settings.get("a").should.equal(123); - settings.get("b").should.equal("test"); - settings.get("c").should.be.an.Array(); - settings.get("c").should.have.lengthOf(3); - - /*jshint immed: false */ - (function() { - settings.a = 456; - }).should.throw(); - - settings.c.push(5); - settings.c.should.be.an.Array(); - settings.c.should.have.lengthOf(4); - - /*jshint immed: false */ - (function() { - settings.set("a",456); - }).should.throw(); - - /*jshint immed: false */ - (function() { - settings.set("a",456); - }).should.throw(); - - /*jshint immed: false */ - (function() { - settings.get("unknown"); - }).should.throw(); - - /*jshint immed: false */ - (function() { - settings.set("unknown",456); - }).should.throw(); - - }); - - it('loads global settings from storage', function(done) { - var userSettings = { - a: 123, - b: "test", - c: [1,2,3] - } - var savedSettings = null; - var saveCount = 0; - var storage = { - getSettings: function() { - return Promise.resolve({globalA:789}); - }, - saveSettings: function(settings) { - saveCount++; - savedSettings = settings; - return Promise.resolve(); - } - } - settings.init(userSettings); - - settings.available().should.be.false(); - - /*jshint immed: false */ - (function() { - settings.get("unknown"); - }).should.throw(); - settings.load(storage).then(function() { - settings.available().should.be.true(); - settings.get("globalA").should.equal(789); - settings.set("globalA","abc").then(function() { - savedSettings.globalA.should.equal("abc"); - saveCount.should.equal(1); - settings.set("globalA","abc").then(function() { - savedSettings.globalA.should.equal("abc"); - // setting to existing value should not trigger save - saveCount.should.equal(1); - done(); - }); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('removes persistent settings when reset', function() { - var userSettings = { - a: 123, - b: "test", - c: [1,2,3] - } - settings.init(userSettings); - - settings.available().should.be.false(); - - settings.should.have.property("a",123); - settings.should.have.property("b","test"); - settings.c.should.be.an.Array(); - settings.c.should.have.lengthOf(3); - - settings.reset(); - - settings.should.not.have.property("a"); - settings.should.not.have.property("d"); - settings.should.not.have.property("c"); - - }); - - it('registers node settings and exports them', function() { - var userSettings = {}; - settings.init(userSettings); - settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}, injectSize:{value:"100", exportable:true}} ); - settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:false}, mqttSize:{value:"50", exportable:true}} ); - settings.registerNodeSettings("http request", {httpRequest1:{value:"a1", exportable:true}} ); - settings.registerNodeSettings(" http--request<> ", {httpRequest2:{value:"a2", exportable:true}} ); - settings.registerNodeSettings("_http_request_", {httpRequest3:{value:"a3", exportable:true}} ); - settings.registerNodeSettings("mQtT", {mQtTColor:{value:"purple", exportable:true}} ); - settings.registerNodeSettings("abc123", {abc123:{value:"def456", exportable:true}} ); - settings.registerNodeSettings("noValue", {noValueHasValue:{value:"123", exportable:true}, noValueNoValue:{exportable:true}} ); - - var safeSettings = {}; - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "red"); - safeSettings.should.have.property("injectSize", "100"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.have.property("mqttSize", "50"); - safeSettings.should.have.property("httpRequest1", "a1"); - safeSettings.should.have.property("httpRequest2", "a2"); - safeSettings.should.have.property("httpRequest3", "a3"); - safeSettings.should.have.property("mQtTColor", "purple"); - safeSettings.should.have.property("abc123", "def456"); - - safeSettings.should.have.property("noValueHasValue", "123"); - safeSettings.should.not.have.property("noValueNoValue"); - }); - - it('prohibits registering the property whose name do not start with type name', function() { - var userSettings = {}; - settings.init(userSettings); - (function() { - settings.registerNodeSettings("inject", {color:{value:"red", exportable:true}} ); - }).should.throw(); - (function() { - settings.registerNodeSettings("_a_b_1_", {ab1Color:{value:"red", exportable:true}} ); - }).should.throw(); - (function() { - settings.registerNodeSettings("AB2", {AB2Color:{value:"red", exportable:true}} ); - }).should.throw(); - (function() { - settings.registerNodeSettings("abcDef", {abcColor:{value:"red", exportable:true}} ); - }).should.throw(); - var safeSettings = {}; - settings.exportNodeSettings(safeSettings); - safeSettings.should.not.have.property("color"); - safeSettings.should.not.have.property("ab1Color", "blue"); - safeSettings.should.not.have.property("AB2Color"); - safeSettings.should.not.have.property("abcColor"); - }); - - it('overwrites node settings with user settings', function() { - var userSettings = { - injectColor: "green", - mqttColor: "yellow", - abColor: [1,2,3] - } - settings.init(userSettings); - settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); - settings.registerNodeSettings("ab", {abColor:{value:"red", exportable:false}} ); - var safeSettings = {}; - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "green"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.not.have.property("abColor"); - }); - - it('disables/enables node settings', function() { - var userSettings = {}; - settings.init(userSettings); - - var safeSettings = {}; - settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); - settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:true}} ); - settings.registerNodeSettings("http request", {httpRequestColor:{value:"yellow", exportable:true}} ); - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "red"); - safeSettings.should.have.property("mqttColor", "purple"); - safeSettings.should.have.property("httpRequestColor", "yellow"); - - safeSettings = {}; - var types = ["inject", "mqtt"]; - settings.disableNodeSettings(types); - settings.exportNodeSettings(safeSettings); - safeSettings.should.not.have.property("injectColor"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.have.property("httpRequestColor", "yellow"); - - safeSettings = {}; - types = ["inject"]; - settings.enableNodeSettings(types); - settings.exportNodeSettings(safeSettings); - safeSettings.should.have.property("injectColor", "red"); - safeSettings.should.not.have.property("mqttColor"); - safeSettings.should.have.property("httpRequestColor", "yellow"); - }); - - - it('delete global setting', function() { - // read-only - var localSettings = {a:1}; - // read-write - var globalSettings = {b:2}; - var storage = { - getSettings: function() { - return Promise.resolve(globalSettings); - }, - saveSettings: function() { - return Promise.resolve(); - } - } - settings.init(localSettings); - return settings.load(storage).then(function() { - settings.get('a').should.eql(1); - settings.get('b').should.eql(2); - return settings.delete('b') - }).then(function() { - should.not.exist(settings.get('b')); - }) - }); - - it('refused to delete local setting', function(done) { - // read-only - var localSettings = {a:1}; - // read-write - var globalSettings = {b:2}; - var storage = { - getSettings: function() { - return Promise.resolve(globalSettings); - } - } - settings.init(localSettings); - settings.load(storage).then(function() { - settings.get('a').should.eql(1); - settings.get('b').should.eql(2); - try { - settings.delete('a'); - return done("Did not throw error"); - } catch(err) { - // expected - } - done(); - }).catch(done) - }); - - - it('get user settings', function() { - var userSettings = { - admin: {a:1} - } - var storage = { - getSettings: function() { - return Promise.resolve({a:1,users:userSettings}); - } - } - settings.init(userSettings); - return settings.load(storage).then(function() { - var result = settings.getUserSettings('admin'); - result.should.eql(userSettings.admin); - // Check it has been cloned - result.should.not.equal(userSettings.admin); - }) - }) - it('set user settings', function() { - var userSettings = { - admin: {a:1} - } - var savedSettings; - var storage = { - getSettings: function() { - return Promise.resolve({c:3,users:userSettings}); - }, - saveSettings: function(s) { - savedSettings = s; - return Promise.resolve(); - } - } - settings.init(userSettings); - return settings.load(storage).then(function() { - return settings.setUserSettings('admin',{b:2}) - }).then(function() { - savedSettings.should.have.property("c",3); - savedSettings.should.have.property('users'); - savedSettings.users.should.eql({admin:{b:2}}) - }) - }) - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/index_spec.js b/test/unit/@node-red/runtime/lib/storage/index_spec.js deleted file mode 100644 index dd9fa6e9b..000000000 --- a/test/unit/@node-red/runtime/lib/storage/index_spec.js +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var paff = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var storage = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/index"); - -describe("red/storage/index", function() { - - it('rejects the promise when settings suggest loading a bad module', function(done) { - - var wrongModule = { - settings:{ - storageModule : "thisaintloading" - } - }; - - storage.init(wrongModule).then( function() { - var one = 1; - var zero = 0; - try { - zero.should.equal(one, "The initialization promise should never get resolved"); - } catch(err) { - done(err); - } - }).catch(function(e) { - done(); //successfully rejected promise - }); - }); - - it('non-string storage module', function(done) { - var initSetsMeToTrue = false; - - var moduleWithBooleanSettingInit = { - init : function() { - initSetsMeToTrue = true; - } - }; - - var setsBooleanModule = { - settings: { - storageModule : moduleWithBooleanSettingInit - } - }; - - storage.init(setsBooleanModule); - initSetsMeToTrue.should.be.true(); - done(); - }); - - it('respects storage interface', function(done) { - var calledFlagGetFlows = false; - var calledFlagGetCredentials = false; - var calledFlagGetAllFlows = false; - var calledInit = false; - var calledFlagGetSettings = false; - var calledFlagGetSessions = false; - - var interfaceCheckerModule = { - init : function (settings) { - settings.should.be.an.Object(); - calledInit = true; - }, - getFlows : function() { - calledFlagGetFlows = true; - return Promise.resolve([]); - }, - saveFlows : function (flows) { - flows.should.be.an.Array(); - flows.should.have.lengthOf(0); - return Promise.resolve(""); - }, - getCredentials : function() { - calledFlagGetCredentials = true; - return Promise.resolve({}); - }, - saveCredentials : function(credentials) { - credentials.should.be.true(); - }, - getSettings : function() { - calledFlagGetSettings = true; - }, - saveSettings : function(settings) { - settings.should.be.true(); - }, - getSessions : function() { - calledFlagGetSessions = true; - }, - saveSessions : function(sessions) { - sessions.should.be.true(); - }, - getAllFlows : function() { - calledFlagGetAllFlows = true; - }, - getFlow : function(fn) { - fn.should.equal("name"); - }, - saveFlow : function(fn, data) { - fn.should.equal("name"); - data.should.be.true(); - }, - getLibraryEntry : function(type, path) { - type.should.be.true(); - path.should.equal("name"); - }, - saveLibraryEntry : function(type, path, meta, body) { - type.should.be.true(); - path.should.equal("name"); - meta.should.be.true(); - body.should.be.true(); - } - }; - - var moduleToLoad = { - settings: { - storageModule : interfaceCheckerModule - } - }; - - var promises = []; - storage.init(moduleToLoad); - promises.push(storage.getFlows()); - promises.push(storage.saveFlows({flows:[],credentials:{}})); - storage.getSettings(); - storage.saveSettings(true); - storage.getSessions(); - storage.saveSessions(true); - storage.getAllFlows(); - storage.getFlow("name"); - storage.saveFlow("name", true); - storage.getLibraryEntry(true, "name"); - storage.saveLibraryEntry(true, "name", true, true); - - Promise.all(promises).then(function() { - try { - calledInit.should.be.true(); - calledFlagGetFlows.should.be.true(); - calledFlagGetCredentials.should.be.true(); - calledFlagGetAllFlows.should.be.true(); - done(); - } catch(err) { - done(err); - } - }); - }); - - describe('respects deprecated flow library functions', function() { - - var savePath; - var saveContent; - var saveMeta; - var saveType; - - var interfaceCheckerModule = { - init : function (settings) { - settings.should.be.an.Object(); - }, - getLibraryEntry : function(type, path) { - if (type === "flows") { - if (path === "/" || path === "\\") { - return Promise.resolve(["a",{fn:"test.json"}]); - } else if (path == "/a" || path == "\\a") { - return Promise.resolve([{fn:"test2.json"}]); - } else if (path == paff.join("","a","test2.json")) { - return Promise.resolve("test content"); - } - } - }, - saveLibraryEntry : function(type, path, meta, body) { - saveType = type; - savePath = path; - saveContent = body; - saveMeta = meta; - return Promise.resolve(); - } - }; - - var moduleToLoad = { - settings: { - storageModule : interfaceCheckerModule - } - }; - before(function() { - storage.init(moduleToLoad); - }); - it('getAllFlows',function(done) { - storage.getAllFlows().then(function (res) { - try { - res.should.eql({ d: { a: { f: ['test2'] } }, f: [ 'test' ] }); - done(); - } catch(err) { - done(err); - } - }); - }); - - it('getFlow',function(done) { - storage.getFlow(paff.join("a","test2.json")).then(function(res) { - try { - res.should.eql("test content"); - done(); - } catch(err) { - done(err); - } - }); - }); - - it ('saveFlow', function (done) { - storage.saveFlow(paff.join("a","test2.json"),"new content").then(function(res) { - try { - savePath.should.eql(paff.join("a","test2.json")); - saveContent.should.eql("new content"); - saveMeta.should.eql({}); - saveType.should.eql("flows"); - done(); - } catch(err) { - done(err); - } - }); - - }); - }); - - describe('handles missing settings/sessions interface', function() { - before(function() { - var interfaceCheckerModule = { - init : function () {} - }; - storage.init({settings:{storageModule: interfaceCheckerModule}}); - }); - - it('defaults missing getSettings',function(done) { - storage.getSettings().then(function(settings) { - should.not.exist(settings); - done(); - }); - }); - it('defaults missing saveSettings',function(done) { - storage.saveSettings({}).then(function() { - done(); - }); - }); - it('defaults missing getSessions',function(done) { - storage.getSessions().then(function(settings) { - should.not.exist(settings); - done(); - }); - }); - it('defaults missing saveSessions',function(done) { - storage.saveSessions({}).then(function() { - done(); - }); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js deleted file mode 100644 index 65826c9f4..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/index_spec.js +++ /dev/null @@ -1,518 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); -var sinon = require('sinon'); -var NR_TEST_UTILS = require("nr-test-utils"); -var process = require("process"); - -var localfilesystem = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem"); -var log = NR_TEST_UTILS.require("@node-red/util").log; - -describe('storage/localfilesystem', function() { - var mockRuntime = { - log:{ - _:function() { return "placeholder message"}, - info: function() { }, - warn: function() { }, - trace: function() {} - } - }; - var userDir = path.join(__dirname,".testUserHome"); - var testFlow = [{"type":"tab","id":"d8be2a6d.2741d8","label":"Sheet 1"}]; - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - - it('should initialise the user directory',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(path.join(userDir,"lib")).should.be.true(); - fs.existsSync(path.join(userDir,"lib",'flows')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - - it('should set userDir to NRH if .config.json presents',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - fs.mkdirSync(process.env.NODE_RED_HOME); - fs.writeFileSync(path.join(process.env.NODE_RED_HOME,".config.json"),"{}","utf8"); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib")).should.be.true(); - fs.existsSync(path.join(process.env.NODE_RED_HOME,"lib",'flows')).should.be.true(); - settings.userDir.should.equal(process.env.NODE_RED_HOME); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should set userDir to HOMEPATH/.node-red if .config.json presents',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - var oldHOMEPATH = process.env.HOMEPATH; - process.env.HOMEPATH = path.join(userDir,"HOMEPATH"); - fs.mkdirSync(process.env.HOMEPATH); - fs.mkdirSync(path.join(process.env.HOMEPATH,".node-red")); - fs.writeFileSync(path.join(process.env.HOMEPATH,".node-red",".config.json"),"{}","utf8"); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib")).should.be.true(); - fs.existsSync(path.join(process.env.HOMEPATH,".node-red","lib",'flows')).should.be.true(); - settings.userDir.should.equal(path.join(process.env.HOMEPATH,".node-red")); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.NODE_HOMEPATH = oldHOMEPATH; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should set userDir to HOME/.node-red',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - var oldHOME = process.env.HOME; - process.env.HOME = path.join(userDir,"HOME"); - var oldHOMEPATH = process.env.HOMEPATH; - process.env.HOMEPATH = path.join(userDir,"HOMEPATH"); - - fs.mkdirSync(process.env.HOME); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.HOME,".node-red","lib")).should.be.true(); - fs.existsSync(path.join(process.env.HOME,".node-red","lib",'flows')).should.be.true(); - settings.userDir.should.equal(path.join(process.env.HOME,".node-red")); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOME = oldHOME; - process.env.HOMEPATH = oldHOMEPATH; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should set userDir to USERPROFILE/.node-red',function(done) { - var oldNRH = process.env.NODE_RED_HOME; - process.env.NODE_RED_HOME = path.join(userDir,"NRH"); - var oldHOME = process.env.HOME; - process.env.HOME = ""; - var oldHOMEPATH = process.env.HOMEPATH; - process.env.HOMEPATH = path.join(userDir,"HOMEPATH"); - var oldUSERPROFILE = process.env.USERPROFILE; - process.env.USERPROFILE = path.join(userDir,"USERPROFILE"); - - fs.mkdirSync(process.env.USERPROFILE); - var settings = {getUserSettings: () => {{}}}; - localfilesystem.init(settings, mockRuntime).then(function() { - try { - fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib")).should.be.true(); - fs.existsSync(path.join(process.env.USERPROFILE,".node-red","lib",'flows')).should.be.true(); - settings.userDir.should.equal(path.join(process.env.USERPROFILE,".node-red")); - done(); - } catch(err) { - done(err); - } finally { - process.env.NODE_RED_HOME = oldNRH; - process.env.HOME = oldHOME; - process.env.HOMEPATH = oldHOMEPATH; - process.env.USERPROFILE = oldUSERPROFILE; - } - }).catch(function(err) { - done(err); - }); - }); - - it('should handle missing flow file',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - fs.existsSync(flowFilePath).should.be.false(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle empty flow file, no backup',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - fs.closeSync(fs.openSync(flowFilePath, 'w')); - fs.existsSync(flowFilePath).should.be.true(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle empty flow file, restores backup',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - fs.closeSync(fs.openSync(flowFilePath, 'w')); - fs.existsSync(flowFilePath).should.be.true(); - fs.existsSync(flowFileBackupPath).should.be.false(); - fs.writeFileSync(flowFileBackupPath,JSON.stringify(testFlow)); - fs.existsSync(flowFileBackupPath).should.be.true(); - setTimeout(function() { - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - },50); - }).catch(function(err) { - done(err); - }); - }); - - it('should save flows to the default file',function(done) { - localfilesystem.init({userDir:userDir,getUserSettings: () => {{}}}, mockRuntime).then(function() { - var flowFile = 'flows_'+require('os').hostname()+'.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - fs.existsSync(flowFilePath).should.be.false(); - fs.existsSync(flowFileBackupPath).should.be.false(); - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(flowFilePath).should.be.true(); - fs.existsSync(flowFileBackupPath).should.be.false(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should save flows to the specified file',function(done) { - var defaultFlowFile = 'flows_'+require('os').hostname()+'.json'; - var defaultFlowFilePath = path.join(userDir,defaultFlowFile); - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.false(); - - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.true(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should format the flows file when flowFilePretty specified',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,flowFilePretty:true,getUserSettings: () => {{}}}, mockRuntime).then(function() { - localfilesystem.saveFlows(testFlow).then(function() { - var content = fs.readFileSync(flowFilePath,"utf8"); - content.split("\n").length.should.be.above(1); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should fsync the flows file',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - localfilesystem.init({editorTheme:{projects:{enabled:false}},userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - sinon.spy(fs,"fsync"); - localfilesystem.saveFlows(testFlow).then(function() { - fs.fsync.callCount.should.be.greaterThan(0); - fs.fsync.restore(); - done(); - }).catch(function(err) { - fs.fsync.restore(); - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should log fsync errors and continue',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - sinon.stub(fs,"fsync").callsFake(function(fd, cb) { - cb(new Error()); - }); - sinon.spy(log,"warn"); - localfilesystem.saveFlows(testFlow).then(function() { - fs.fsync.callCount.should.be.greaterThan(0); - log.warn.restore(); - fs.fsync.callCount.should.be.greaterThan(0); - fs.fsync.restore(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should backup the flows file', function(done) { - var defaultFlowFile = 'flows_'+require('os').hostname()+'.json'; - var defaultFlowFilePath = path.join(userDir,defaultFlowFile); - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var flowFileBackupPath = path.join(userDir,"."+flowFile+".backup"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.false(); - fs.existsSync(flowFileBackupPath).should.be.false(); - - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(flowFileBackupPath).should.be.false(); - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.true(); - var content = fs.readFileSync(flowFilePath,'utf8'); - var testFlow2 = [{"type":"tab","id":"bc5672ad.2741d8","label":"Sheet 2"}]; - - localfilesystem.saveFlows(testFlow2).then(function() { - fs.existsSync(flowFileBackupPath).should.be.true(); - fs.existsSync(defaultFlowFilePath).should.be.false(); - fs.existsSync(flowFilePath).should.be.true(); - var backupContent = fs.readFileSync(flowFileBackupPath,'utf8'); - content.should.equal(backupContent); - var content2 = fs.readFileSync(flowFilePath,'utf8'); - content2.should.not.equal(backupContent); - done(); - - }).catch(function(err) { - done(err); - }); - - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - - - }); - - it('should handle missing credentials', function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - fs.existsSync(credFile).should.be.false(); - - localfilesystem.getCredentials().then(function(creds) { - creds.should.eql({}); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle credentials', function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - - fs.existsSync(credFile).should.be.false(); - - var credentials = {"abc":{"type":"creds"}}; - - localfilesystem.saveCredentials(credentials).then(function() { - fs.existsSync(credFile).should.be.true(); - localfilesystem.getCredentials().then(function(creds) { - creds.should.eql(credentials); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - - it('should backup existing credentials', function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - var credFileBackup = path.join(userDir,".test_cred.json.backup"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath,getUserSettings: () => {{}}}, mockRuntime).then(function() { - - fs.writeFileSync(credFile,"{}","utf8"); - - fs.existsSync(credFile).should.be.true(); - fs.existsSync(credFileBackup).should.be.false(); - - var credentials = {"abc":{"type":"creds"}}; - - localfilesystem.saveCredentials(credentials).then(function() { - fs.existsSync(credFile).should.be.true(); - fs.existsSync(credFileBackup).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should format the creds file when flowFilePretty specified',function(done) { - var flowFile = 'test.json'; - var flowFilePath = path.join(userDir,flowFile); - var credFile = path.join(userDir,"test_cred.json"); - - localfilesystem.init({userDir:userDir, flowFile:flowFilePath, flowFilePretty:true,getUserSettings: () => {{}}}, mockRuntime).then(function() { - - fs.existsSync(credFile).should.be.false(); - - var credentials = {"abc":{"type":"creds"}}; - - localfilesystem.saveCredentials(credentials).then(function() { - fs.existsSync(credFile).should.be.true(); - var content = fs.readFileSync(credFile,"utf8"); - content.split("\n").length.should.be.above(1); - localfilesystem.getCredentials().then(function(creds) { - creds.should.eql(credentials); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle flow file in random unc path and non-existent subfolder',function(done) { - // only test on win32 - if (process.platform !== 'win32') { - console.log('skipped test as not win32'); - done(); - return; - } - - // get a real windows path - var flowFile = path.win32.resolve(userDir+'/some/random/path'); - var rootdir = path.win32.resolve(userDir+'/some'); - // make it into a local UNC path - flowFile = flowFile.replace('C:\\', '\\\\localhost\\c$\\'); - localfilesystem.init({userDir:userDir, flowFile:flowFile}, mockRuntime).then(function() { - fs.existsSync(flowFile).should.be.false(); - localfilesystem.saveFlows(testFlow).then(function() { - fs.existsSync(flowFile).should.be.true(); - localfilesystem.getFlows().then(function(flows) { - flows.should.eql(testFlow); - // cleanup - fs.removeSync(rootdir); - done(); - }).catch(function(err) { - // cleanup - fs.removeSync(rootdir); - done(err); - }); - }).catch(function(err) { - // cleanup - fs.removeSync(rootdir); - done(err); - }); - }).catch(function(err) { - // cleanup - fs.removeSync(rootdir); - done(err); - }); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js deleted file mode 100644 index 69b6e3da6..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/library_spec.js +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); -var NR_TEST_UTILS = require("nr-test-utils"); - -var localfilesystemLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/library"); - -describe('storage/localfilesystem/library', function() { - var userDir = path.join(__dirname,".testUserHome"); - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - - it('should return an empty list of library objects',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - localfilesystemLibrary.getLibraryEntry('object','').then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return an empty list of library objects (path=/)',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - localfilesystemLibrary.getLibraryEntry('object','/').then(function(flows) { - flows.should.eql([]); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return an error for a non-existent library object',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - localfilesystemLibrary.getLibraryEntry('object','A/B').then(function(flows) { - should.fail(null,null,"non-existent flow"); - }).catch(function(err) { - should.exist(err); - done(); - }); - }).catch(function(err) { - done(err); - }); - }); - - function createObjectLibrary(type) { - type = type || "object"; - var objLib = path.join(userDir, "lib", type); - try { - fs.mkdirSync(objLib); - } catch (err) { - } - fs.mkdirSync(path.join(objLib, "A")); - fs.mkdirSync(path.join(objLib, "B")); - fs.mkdirSync(path.join(objLib, "B", "C")); - fs.mkdirSync(path.join(objLib, "D")); - if (type === "functions" || type === "object") { - fs.writeFileSync(path.join(objLib, "file1.js"), "// abc: def\n// not a metaline \n\n Hi", 'utf8'); - fs.writeFileSync(path.join(objLib, "B", "file2.js"), "// ghi: jkl\n// not a metaline \n\n Hi", 'utf8'); - fs.writeFileSync(path.join(objLib, "D", "file3.js"), "// mno: 日本語テスト\n\nこんにちわ", 'utf8'); - } - if (type === "flows" || type === "object") { - fs.writeFileSync(path.join(objLib, "B", "flow.json"), "Hi", 'utf8'); - } - } - - it('should return a directory listing of library objects', function (done) { - localfilesystemLibrary.init({userDir: userDir}).then(function () { - createObjectLibrary(); - - localfilesystemLibrary.getLibraryEntry('object', '').then(function (flows) { - flows.should.eql([ 'A', 'B', 'D', { abc: 'def', fn: 'file1.js' }]); - localfilesystemLibrary.getLibraryEntry('object', 'B').then(function (flows) { - flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' }, { fn: 'flow.json' }]); - localfilesystemLibrary.getLibraryEntry('object', 'B/C').then(function (flows) { - flows.should.eql([]); - localfilesystemLibrary.getLibraryEntry('object', 'D').then(function (flows) { - flows.should.eql([{ mno: '日本語テスト', fn: 'file3.js' }]); - done(); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }).catch(function (err) { - done(err); - }); - }); - - it('should load a flow library object with .json unspecified', function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("flows"); - localfilesystemLibrary.getLibraryEntry('flows','B/flow').then(function(flows) { - flows.should.eql("Hi"); - done(); - }).catch(function(err) { - done(err); - }); - }); - - }); - - it('should return a library object',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary(); - localfilesystemLibrary.getLibraryEntry('object','B/file2.js').then(function(body) { - body.should.eql("// not a metaline \n\n Hi"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return a newly saved library function',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("functions"); - localfilesystemLibrary.getLibraryEntry('functions','B').then(function(flows) { - flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' } ]); - var ft = path.join("B","D","file3.js"); - localfilesystemLibrary.saveLibraryEntry('functions',ft,{mno:'pqr'},"// another non meta line\n\n Hi There").then(function() { - setTimeout(function() { - localfilesystemLibrary.getLibraryEntry('functions',path.join("B","D")).then(function(flows) { - flows.should.eql([ { mno: 'pqr', fn: 'file3.js' } ]); - localfilesystemLibrary.getLibraryEntry('functions',ft).then(function(body) { - body.should.eql("// another non meta line\n\n Hi There"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }) - }, 50); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return a newly saved library flow',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("flows"); - localfilesystemLibrary.getLibraryEntry('flows','B').then(function(flows) { - flows.should.eql([ 'C', {fn:'flow.json'} ]); - var ft = path.join("B","D","file3"); - localfilesystemLibrary.saveLibraryEntry('flows',ft,{mno:'pqr'},"Hi").then(function() { - setTimeout(function() { - localfilesystemLibrary.getLibraryEntry('flows',path.join("B","D")).then(function(flows) { - flows.should.eql([ { mno: 'pqr', fn: 'file3.json' } ]); - localfilesystemLibrary.getLibraryEntry('flows',ft+".json").then(function(body) { - body.should.eql("Hi"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }) - }, 50); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should return a newly saved library flow (multi-byte character)',function(done) { - localfilesystemLibrary.init({userDir:userDir}).then(function() { - createObjectLibrary("flows"); - localfilesystemLibrary.getLibraryEntry('flows','B').then(function(flows) { - flows.should.eql([ 'C', {fn:'flow.json'} ]); - var ft = path.join("B","D","file4"); - localfilesystemLibrary.saveLibraryEntry('flows',ft,{mno:'pqr'},"こんにちわこんにちわこんにちわ").then(function() { - setTimeout(function() { - localfilesystemLibrary.getLibraryEntry('flows',path.join("B","D")).then(function(flows) { - flows.should.eql([ { mno: 'pqr', fn: 'file4.json' } ]); - localfilesystemLibrary.getLibraryEntry('flows',ft+".json").then(function(body) { - body.should.eql("こんにちわこんにちわこんにちわ"); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }) - }, 50); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js deleted file mode 100644 index ecf24cf40..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/Project_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("storage/localfilesystem/projects/Project", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}) diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js deleted file mode 100644 index 3fab5a45c..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet_spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - -var should = require("should"); -var NR_TEST_UTILS = require("nr-test-utils"); -var defaultFileSet = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/defaultFileSet"); - -describe('storage/localfilesystem/projects/defaultFileSet', function() { - var runtime = { - i18n: { - "_": function(name) { - return name; - } - } - }; - it('generates package.json for a project', function() { - var generated = defaultFileSet["package.json"]({ - name: "A TEST NAME", - summary: "A TEST SUMMARY", - files: { - flow: "MY FLOW FILE", - credentials: "MY CREDENTIALS FILE" - } - }, runtime); - - var parsed = JSON.parse(generated); - parsed.should.have.property('name',"A TEST NAME"); - parsed.should.have.property('description',"A TEST SUMMARY"); - parsed.should.have.property('node-red'); - parsed['node-red'].should.have.property('settings'); - parsed['node-red'].settings.should.have.property('flowFile',"MY FLOW FILE"); - parsed['node-red'].settings.should.have.property('credentialsFile',"MY CREDENTIALS FILE"); - }); - - it('generates README.md for a project', function() { - var generated = defaultFileSet["README.md"]({ - name: "A TEST NAME", - summary: "A TEST SUMMARY" - }, runtime); - generated.should.match(/A TEST NAME/); - generated.should.match(/A TEST SUMMARY/); - }); - it('generates .gitignore for a project', function() { - var generated = defaultFileSet[".gitignore"]({ - name: "A TEST NAME", - summary: "A TEST SUMMARY" - }, runtime); - generated.length.should.be.greaterThan(0); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js deleted file mode 100644 index c1617bf90..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache_spec.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var authCache = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/git/authCache") - -describe("localfilesystem/projects/git/authCache", function() { - - beforeEach(function() { - authCache.init(); - }); - afterEach(function() { - authCache.init(); - }); - - it('sets/clears auth details for a given project/remote/user', function() { - should.not.exist(authCache.get("project","remote1","user1")); - should.not.exist(authCache.get("project","remote1","user2")); - - authCache.set("project","remote1","user1",{foo1:"bar1"}); - authCache.set("project","remote1","user2",{foo2:"bar2"}); - - var result = authCache.get("project","remote1","user1"); - result.should.have.property("foo1","bar1"); - - result = authCache.get("project","remote1","user2"); - result.should.have.property("foo2","bar2"); - - authCache.clear("project","remote1","user1"); - should.not.exist(authCache.get("project","remote1","user1")); - should.exist(authCache.get("project","remote1","user2")); - - }); - - - it('clears auth details for all users on a given project/remote', function() { - - authCache.set("project","remote1","user1",{foo1:"bar1"}); - authCache.set("project","remote1","user2",{foo2:"bar2"}); - authCache.set("project","remote2","user1",{foo3:"bar3"}); - - should.exist(authCache.get("project","remote1","user1")); - should.exist(authCache.get("project","remote1","user2")); - should.exist(authCache.get("project","remote2","user1")); - - authCache.clear("project","remote1"); - should.not.exist(authCache.get("project","remote1","user1")); - should.not.exist(authCache.get("project","remote1","user2")); - should.exist(authCache.get("project","remote2","user1")); - }); - - it('clears auth details for all remotes/users on a given project', function() { - - authCache.set("project1","remote1","user1",{foo1:"bar1"}); - authCache.set("project1","remote1","user2",{foo2:"bar2"}); - authCache.set("project2","remote2","user1",{foo3:"bar3"}); - - should.exist(authCache.get("project1","remote1","user1")); - should.exist(authCache.get("project1","remote1","user2")); - should.exist(authCache.get("project2","remote2","user1")); - - authCache.clear("project2"); - should.exist(authCache.get("project1","remote1","user1")); - should.exist(authCache.get("project1","remote1","user2")); - should.not.exist(authCache.get("project2","remote2","user1")); - }); - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js deleted file mode 100644 index 9b789a66b..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer_spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var path = require("path"); -var os = require("os"); -var should = require("should"); -var sinon = require("sinon"); -var child_process = require("child_process"); -var fs = require("fs-extra"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var authServer = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/git/authServer"); - - -var sendPrompt = function(localPath, prompt) { - return new Promise(function(resolve,reject) { - var response; - var socket = net.connect(localPath, function() { - socket.on('data', function(data) { response = data; socket.end() }); - socket.on('end', function() { - resolve(response); - }); - socket.on('error',reject); - socket.write(prompt+"\n", 'utf8'); - }); - socket.setEncoding('utf8'); - }); -} - - -describe("localfilesystem/projects/git/authServer", function() { - it("listens for user/pass prompts and returns provided auth", function(done) { - authServer.ResponseServer({username: "TEST_USER", password: "TEST_PASS"}).then(function(rs) { - sendPrompt(rs.path,"Username").then(function(response) { - response.should.eql("TEST_USER"); - return sendPrompt(rs.path,"Password"); - }).then(function(response) { - response.should.eql("TEST_PASS"); - }).then(() => { - rs.close(); - done(); - }).catch(function(err) { - rs.close(); - done(err); - }) - - }) - }); - - it("listens for ssh prompts and returns provided auth", function(done) { - authServer.ResponseSSHServer({passphrase: "TEST_PASSPHRASE"}).then(function(rs) { - sendPrompt(rs.path,"The").then(function(response) { - // TODO: - response.should.eql("yes"); - return sendPrompt(rs.path,"Enter"); - }).then(function(response) { - response.should.eql("TEST_PASSPHRASE"); - }).then(() => { - rs.close(); - done(); - }).catch(function(err) { - rs.close(); - done(err); - }) - - }) - }) -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js deleted file mode 100644 index c80cb8ad1..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter_spec.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var net = require("net"); -var path = require("path"); -var os = require("os"); -var should = require("should"); -var sinon = require("sinon"); -var child_process = require("child_process"); -var fs = require("fs-extra"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var authWriter = NR_TEST_UTILS.resolve("@node-red/runtime/lib/storage/localfilesystem/projects/git/authWriter"); - -function getListenPath() { - var seed = (0x100000+Math.random()*0x999999).toString(16); - var fn = 'node-red-git-askpass-'+seed+'-sock'; - var listenPath; - if (process.platform === 'win32') { - listenPath = '\\\\.\\pipe\\'+fn; - } else { - listenPath = path.join(process.env['XDG_RUNTIME_DIR'] || os.tmpdir(), fn); - } - // console.log(listenPath); - return listenPath; -} - - -describe("localfilesystem/projects/git/authWriter", function() { - it("connects to port and sends passphrase", function(done) { - var receivedData = ""; - var server = net.createServer(function(connection) { - connection.setEncoding('utf8'); - connection.on('data', function(data) { - receivedData += data; - var m = data.indexOf("\n"); - if (m !== -1) { - connection.end(); - } - }); - }); - - var listenPath = getListenPath(); - - server.listen(listenPath, function(ready) { - child_process.exec('"'+process.execPath+'" "'+authWriter+'" "'+listenPath+'" TEST_PHRASE_FOO',{cwd:__dirname}, (error,stdout,stderr) => { - server.close(); - try { - should.not.exist(error); - receivedData.should.eql("TEST_PHRASE_FOO\n"); - done(); - } catch(err) { - done(err); - } - }); - }); - server.on('close', function() { - // console.log("Closing response server"); - fs.removeSync(listenPath); - }); - server.on('error',function(err) { - console.log("ResponseServer unexpectedError:",err.toString()); - server.close(); - done(err); - }); - - - }) -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js deleted file mode 100644 index 773342fa6..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/git/index_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("storage/localfilesystem/projects/git/index", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}) diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js deleted file mode 100644 index f63a20522..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/index_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("storage/localfilesystem/projects/index", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}) diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js deleted file mode 100644 index c6d0c533e..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/index_spec.js +++ /dev/null @@ -1,433 +0,0 @@ -/** -* Copyright JS Foundation and other contributors, http://js.foundation -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); -var sshkeys = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/ssh"); - -describe("storage/localfilesystem/projects/ssh", function() { - var userDir = path.join(__dirname,".testSSHKeyUserHome"); - var mockSettings = { - userDir: userDir - }; - var mockRuntime = { - log:{ - _:function() { return "placeholder message"}, - info: function() { }, - log: function() { }, - trace: function() { } - } - }; - var oldHOME; - - beforeEach(function(done) { - oldHOME = process.env.HOME; - process.env.HOME = "/tmp/doesnt/exist"; - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - process.env.HOME = oldHOME; - fs.remove(userDir,done); - }); - - it('should create sshkey directory when sshkey initializes', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - sshkeys.init(mockSettings, mockRuntime).then(function() { - var ret = fs.existsSync(sshkeyDirPath); - ret.should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey empty list if there is no sshkey file', function(done) { - var username = 'test'; - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(0); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey list', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filenameList = ['test-key01', 'test-key02']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not get sshkey file if there is only private key', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filenameList = ['test-key01', 'test-key02']; - var onlyPrivateKeyFilenameList = ['test-key03', 'test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of onlyPrivateKeyFilenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var filename of onlyPrivateKeyFilenameList) { - retObj.should.not.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not get sshkey file if there is only public key', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filenameList = ['test-key01', 'test-key02']; - var directoryList = ['test-key03', '.test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of directoryList) { - fs.ensureDirSync(path.join(sshkeyDirPath,filename)); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var directoryname of directoryList) { - retObj.should.not.containEql({ name: directoryname }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey list that does not have directory', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var otherUsername = 'other'; - var filenameList = ['test-key01', 'test-key02']; - var otherUserFilenameList = ['test-key03', 'test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of otherUserFilenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename+".pub"),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var filename of otherUserFilenameList) { - retObj.should.not.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey list that have keys of specified user', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var otherUsername = 'other'; - var filenameList = ['test-key01', 'test-key02']; - var otherUserFilenameList = ['test-key03', 'test-key04']; - sshkeys.init(mockSettings, mockRuntime).then(function() { - for(var filename of filenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),"","utf8"); - } - for(var filename of otherUserFilenameList) { - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,otherUsername+"_"+filename+".pub"),"","utf8"); - } - sshkeys.listSSHKeys(username).then(function(retObj) { - retObj.should.be.instanceOf(Array).and.have.lengthOf(filenameList.length); - for(var filename of filenameList) { - retObj.should.containEql({ name: filename }); - } - for(var filename of otherUserFilenameList) { - retObj.should.not.containEql({ name: filename }); - } - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with empty data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - name: 'test-key01' - }; - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with only comment data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01' - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with password data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - password: 'testtest' - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with size data', function(done) { - this.timeout(20000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - size: 4096 - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should generate sshkey file with password & size data', function(done) { - this.timeout(20000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - password: 'testtest', - size: 4096 - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - retObj.should.be.equal(options.name); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name)).should.be.true(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+options.name+'.pub')).should.be.true(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not generate sshkey file with illegal size data', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - size: 1023 - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - done(new Error('Does NOT throw error!')); - }).catch(function(err) { - try { - err.should.have.property('code', 'key_length_too_short'); - done(); - } - catch (error) { - done(error); - } - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should not generate sshkey file with illegal password', function(done) { - this.timeout(10000); - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var options = { - comment: 'test@test.com', - name: 'test-key01', - password: 'aa' - }; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - sshkeys.generateSSHKey(username, options).then(function(retObj) { - done(new Error('Does NOT throw error!')); - }).catch(function(err) { - try { - err.should.have.property('code', 'key_passphrase_too_short'); - done(); - } - catch (error) { - done(error); - } - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should get sshkey file content', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filename = 'test-key01'; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),fileContent,"utf8"); - sshkeys.getSSHKey(username, filename).then(function(retObj) { - retObj.should.be.equal(fileContent); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); - - it('should delete sshkey files', function(done) { - var sshkeyDirPath = path.join(userDir, 'projects', '.sshkeys'); - var username = 'test'; - var filename = 'test-key01'; - var fileContent = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD3a+sgtgzSbbliWxmOq5p6+H/mE+0gjWfLWrkIVmHENd1mifV4uCmIHAR2NfuadUYMQ3+bQ90kpmmEKTMYPsyentsKpHQZxTzG7wOCAIpJnbPTHDMxEJhVTaAwEjbVyMSIzTTPfnhoavWIBu0+uMgKDDlBm+RjlgkFlyhXyCN6UwFrIUUMH6Gw+eQHLiooKIl8ce7uDxIlt+9b7hFCU+sQ3kvuse239DZluu6+8buMWqJvrEHgzS9adRFKku8nSPAEPYn85vDi7OgVAcLQufknNgs47KHBAx9h04LeSrFJ/P5J1b//ItRpMOIme+O9d1BR46puzhvUaCHLdvO9czj+OmW+dIm+QIk6lZIOOMnppG72kZxtLfeKT16ur+2FbwAdL9ItBp4BI/YTlBPoa5mLMxpuWfmX1qHntvtGc9wEwS1P7YFfmF3XiK5apxalzrn0Qlr5UmDNbVIqJb1OlbC0w03Z0oktti1xT+R2DGOLWM4lBbpXDHV1BhQ7oYOvbUD8Cnof55lTP0WHHsOHlQc/BGDti1XA9aBX/OzVyzBUYEf0pkimsD0RYo6aqt7QwehJYdlz9x1NBguBffT0s4NhNb9IWr+ASnFPvNl2sw4XH/8U0J0q8ZkMpKkbLM1Zdp1Fv00GF0f5UNRokai6uM3w/ccantJ3WvZ6GtctqytWrw== \n"; - - sshkeys.init(mockSettings, mockRuntime).then(function() { - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename),"","utf8"); - fs.writeFileSync(path.join(sshkeyDirPath,username+"_"+filename+".pub"),fileContent,"utf8"); - sshkeys.deleteSSHKey(username, filename).then(function() { - fs.existsSync(path.join(sshkeyDirPath,username+'_'+filename)).should.be.false(); - fs.existsSync(path.join(sshkeyDirPath,username+'_'+filename+'.pub')).should.be.false(); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js deleted file mode 100644 index f3487277b..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen_spec.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var sinon = require("sinon"); -var child_process = require('child_process'); -var EventEmitter = require("events"); - -var NR_TEST_UTILS = require("nr-test-utils"); -var keygen = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/projects/ssh/keygen") - -describe("localfilesystem/projects/ssh/keygen", function() { - - afterEach(function() { - if (child_process.spawn.restore) { - child_process.spawn.restore(); - } - }) - - it("invokes sshkeygen", function(done) { - var command; - var args; - var opts; - sinon.stub(child_process,"spawn").callsFake(function(_command,_args,_opts) { - _command = command; - _args = args; - _opts = opts; - - var e = new EventEmitter(); - e.stdout = new EventEmitter(); - e.stderr = new EventEmitter(); - setTimeout(function() { - e.stdout.emit("data","result"); - e.emit("close",0); - },50) - return e; - }); - - keygen.generateKey({ - size: 1024, - location: 'location', - comment: 'comment', - password: 'password' - }).then(function(output) { - output.should.equal("result"); - done(); - }).catch(function(err) { - done(err); - }) - }) - - it("reports passphrase too short", function(done) { - var command; - var args; - var opts; - - try { - keygen.generateKey({ - size: 1024, - location: 'location', - comment: 'comment', - password: '123' - }).then(function(output) { - done(new Error("Error not thrown")); - }).catch(function(err) { - done(new Error("Error not thrown")); - }) - } catch(err) { - err.should.have.property("code","key_passphrase_too_short"); - done(); - } - }); - - it("reports key length too short", function(done) { - var command; - var args; - var opts; - try { - keygen.generateKey({ - size: 123, - location: 'location', - comment: 'comment', - password: 'password' - }).then(function(output) { - done(new Error("Error not thrown")); - }).catch(function(err) { - done(new Error("Error not thrown")); - }) - } catch(err) { - err.should.have.property("code","key_length_too_short"); - done(); - - } - }); - - -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js deleted file mode 100644 index 685886d67..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/sessions_spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var fs = require('fs-extra'); -var path = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); -var localfilesystemSessions = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/sessions"); - -describe('storage/localfilesystem/sessions', function() { - var userDir = path.join(__dirname,".testUserHome"); - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - it('should handle non-existent sessions', function(done) { - var sessionsFile = path.join(userDir,".sessions.json"); - - localfilesystemSessions.init({userDir:userDir}); - fs.existsSync(sessionsFile).should.be.false(); - localfilesystemSessions.getSessions().then(function(sessions) { - sessions.should.eql({}); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle corrupt sessions', function(done) { - var sessionsFile = path.join(userDir,".sessions.json"); - fs.writeFileSync(sessionsFile,"[This is not json","utf8"); - localfilesystemSessions.init({userDir:userDir}); - fs.existsSync(sessionsFile).should.be.true(); - localfilesystemSessions.getSessions().then(function(sessions) { - sessions.should.eql({}); - done(); - }).catch(function(err) { - done(err); - }); - }); - - it('should handle sessions', function(done) { - var sessionsFile = path.join(userDir,".sessions.json"); - - localfilesystemSessions.init({userDir:userDir}); - fs.existsSync(sessionsFile).should.be.false(); - - var sessions = {"abc":{"type":"creds"}}; - - localfilesystemSessions.saveSessions(sessions).then(function() { - fs.existsSync(sessionsFile).should.be.true(); - localfilesystemSessions.getSessions().then(function(_sessions) { - _sessions.should.eql(sessions); - done(); - }).catch(function(err) { - done(err); - }); - }).catch(function(err) { - done(err); - }); - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js deleted file mode 100644 index bd07ca27f..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/settings_spec.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -const should = require("should"); -const fs = require('fs-extra'); -const path = require('path'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var localfilesystemSettings = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/settings"); - -describe('storage/localfilesystem/settings', function() { - var userDir = path.join(__dirname,".testUserHome"); - beforeEach(function(done) { - fs.remove(userDir,function(err) { - fs.mkdir(userDir,done); - }); - }); - afterEach(function(done) { - fs.remove(userDir,done); - }); - - it('should handle non-existent settings', function(done) { - var settingsFile = path.join(userDir,".config.json"); - localfilesystemSettings.init({userDir:userDir}).then(function() { - fs.existsSync(settingsFile).should.be.false(); - return localfilesystemSettings.getSettings(); - }).then(function(settings) { - settings.should.eql({}); - done(); - }).catch(err => { done(err)}); - }); - - it('should migrate single config.json to multiple files', function(done) { - var settingsFile = path.join(userDir,".config.json"); - fs.writeFileSync(settingsFile,JSON.stringify({ - nodes:{a:1}, - _credentialSecret: "foo", - users:{b:2}, - projects: {c:3} - }),"utf8"); - - async function checkFile(sectionName, expectedContents) { - const file = path.join(userDir,".config."+sectionName+".json"); - fs.existsSync(file).should.be.true(); - var contents = await fs.readFile(file,'utf8'); - var data = JSON.parse(contents); - data.should.eql(expectedContents) - } - - localfilesystemSettings.init({userDir:userDir}).then(async function() { - // (For now) leave the old settings file in place - fs.existsSync(settingsFile).should.be.true(); - await checkFile("nodes",{a:1}) - await checkFile("users",{b:2}) - await checkFile("projects",{c:3}) - await checkFile("runtime",{_credentialSecret:"foo"}) - done(); - }).catch(err => { done(err)}); - }); - - it('should load separate settings file', async function() { - await fs.writeFile( path.join(userDir,".config.nodes.json"),JSON.stringify({a:1}),"utf8"); - await fs.writeFile( path.join(userDir,".config.users.json"),JSON.stringify({b:2}),"utf8"); - await fs.writeFile( path.join(userDir,".config.projects.json"),JSON.stringify({c:3}),"utf8"); - await fs.writeFile( path.join(userDir,".config.runtime.json"),JSON.stringify({_credentialSecret:"foo"}),"utf8"); - - return localfilesystemSettings.init({userDir:userDir}) - .then(localfilesystemSettings.getSettings) - .then(settings => { - settings.should.eql({ - nodes:{a:1}, - _credentialSecret: "foo", - users:{b:2}, - projects: {c:3} - }) - }) - }); - - it('should write only the files that need writing', async function() { - await fs.writeFile( path.join(userDir,".config.nodes.json"),JSON.stringify({a:1}),"utf8"); - await fs.writeFile( path.join(userDir,".config.users.json"),JSON.stringify({b:2}),"utf8"); - await fs.writeFile( path.join(userDir,".config.projects.json"),JSON.stringify({c:3}),"utf8"); - await fs.writeFile( path.join(userDir,".config.runtime.json"),JSON.stringify({_credentialSecret:"foo"}),"utf8"); - - const fsStatNodes = await fs.stat(path.join(userDir,".config.nodes.json")) - const fsStatUsers = await fs.stat(path.join(userDir,".config.users.json")) - const fsStatProjects = await fs.stat(path.join(userDir,".config.projects.json")) - const fsStatRuntime = await fs.stat(path.join(userDir,".config.runtime.json")) - - return localfilesystemSettings.init({userDir:userDir}).then(function() { - return new Promise(res => { - setTimeout(function() { - res(); - },10) - }); - }).then(() => { - return localfilesystemSettings.saveSettings({ - nodes:{d:4}, - _credentialSecret: "bar", - users:{b:2}, - projects: {c:3} - }) - }).then(async function() { - - const newFsStatNodes = await fs.stat(path.join(userDir,".config.nodes.json")) - const newFsStatUsers = await fs.stat(path.join(userDir,".config.users.json")) - const newFsStatProjects = await fs.stat(path.join(userDir,".config.projects.json")) - const newFsStatRuntime = await fs.stat(path.join(userDir,".config.runtime.json")) - - // Not changed - newFsStatUsers.mtimeMs.should.eql(fsStatUsers.mtimeMs); - newFsStatProjects.mtimeMs.should.eql(fsStatProjects.mtimeMs); - - // Changed - newFsStatNodes.mtimeMs.should.not.eql(fsStatNodes.mtimeMs); - newFsStatRuntime.mtimeMs.should.not.eql(fsStatRuntime.mtimeMs); - - }) - }); -}); diff --git a/test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js b/test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js deleted file mode 100644 index fa1e4bdb7..000000000 --- a/test/unit/@node-red/runtime/lib/storage/localfilesystem/util_spec.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var should = require("should"); -var NR_TEST_UTILS = require("nr-test-utils"); -var util = NR_TEST_UTILS.require("@node-red/runtime/lib/storage/localfilesystem/util"); - -describe('storage/localfilesystem/util', function() { - describe('parseJSON', function() { - it('returns parsed JSON', function() { - var result = util.parseJSON('{"a":123}'); - result.should.eql({a:123}); - }) - it('ignores BOM character', function() { - var result = util.parseJSON('\uFEFF{"a":123}'); - result.should.eql({a:123}); - }) - }) -}); diff --git a/test/unit/@node-red/util/index_spec.js b/test/unit/@node-red/util/index_spec.js deleted file mode 100644 index ee46fc504..000000000 --- a/test/unit/@node-red/util/index_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("node-red/red", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}); diff --git a/test/unit/@node-red/util/lib/events_spec.js b/test/unit/@node-red/util/lib/events_spec.js deleted file mode 100644 index 09f5d2ae0..000000000 --- a/test/unit/@node-red/util/lib/events_spec.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("@node-red/util/events", function() { - it('can be required without errors', function() { - NR_TEST_UTILS.require("@node-red/util/lib/events"); - }); - it.skip('more tests needed', function(){}) -}); diff --git a/test/unit/@node-red/util/lib/exec_spec.js b/test/unit/@node-red/util/lib/exec_spec.js deleted file mode 100644 index 9b02fb3f1..000000000 --- a/test/unit/@node-red/util/lib/exec_spec.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var path = require("path"); -var EventEmitter = require("events").EventEmitter; - - -var child_process = require('child_process'); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var events = NR_TEST_UTILS.require("@node-red/util/lib/events"); -var exec = NR_TEST_UTILS.require("@node-red/util/lib/exec"); - -describe("runtime/exec", function() { - var logEvents; - var mockProcess; - const eventLogHandler = function(ev) { - logEvents.push(ev); - } - - beforeEach(function() { - - logEvents = []; - events.on("event-log", eventLogHandler); - - mockProcess = new EventEmitter(); - mockProcess.stdout = new EventEmitter(); - mockProcess.stderr = new EventEmitter(); - sinon.stub(child_process,'spawn').callsFake(function(command,args,options) { - mockProcess._args = {command,args,options}; - return mockProcess; - }); - }); - - afterEach(function() { - events.removeListener("event-log", eventLogHandler); - if (child_process.spawn.restore) { - child_process.spawn.restore(); - } - }); - - it("runs command and resolves on success - no emit", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts).then(function(result) { - command.should.eql(mockProcess._args.command); - args.should.eql(mockProcess._args.args); - opts.should.eql(mockProcess._args.options); - logEvents.length.should.eql(0); - result.code.should.eql(0); - result.stdout.should.eql("123"); - result.stderr.should.eql("abc"); - done(); - }).catch(done); - - mockProcess.stdout.emit('data',"1"); - mockProcess.stderr.emit('data',"a"); - mockProcess.stderr.emit('data',"b"); - mockProcess.stdout.emit('data',"2"); - mockProcess.stdout.emit('data',"3"); - mockProcess.stderr.emit('data',"c"); - mockProcess.emit('close',0); - }); - - it("runs command and resolves on success - emit", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts,true).then(function(result) { - logEvents.length.should.eql(8); - done(); - }).catch(done); - - mockProcess.stdout.emit('data',"1"); - mockProcess.stderr.emit('data',"a"); - mockProcess.stderr.emit('data',"b"); - mockProcess.stdout.emit('data',"2"); - mockProcess.stdout.emit('data',"3"); - mockProcess.stderr.emit('data',"c"); - mockProcess.emit('close',0); - }) - - it("runs command and rejects on error - close", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts).then(function() { - done("Command should have rejected"); - }).catch(function(result) { - result - result.code.should.eql(123); - result.stdout.should.eql("123"); - result.stderr.should.eql("abc"); - done(); - }).catch(done); - - mockProcess.stdout.emit('data',"1"); - mockProcess.stderr.emit('data',"a"); - mockProcess.stderr.emit('data',"b"); - mockProcess.stdout.emit('data',"2"); - mockProcess.stdout.emit('data',"3"); - mockProcess.stderr.emit('data',"c"); - mockProcess.emit('close',123); - }) - - it("runs command and rejects on error - error", function(done) { - var command = "cmd"; - var args = [1,2,3]; - var opts = { a: true }; - exec.run(command,args,opts).then(function() { - done("Command should have rejected"); - }).catch(function(result) { - result - result.code.should.eql(456); - result.stdout.should.eql(""); - result.stderr.should.eql("test-error"); - done(); - }).catch(done); - - mockProcess.emit('error',"test-error"); - mockProcess.emit('close',456); - }) - -}); diff --git a/test/unit/@node-red/util/lib/hooks_spec.js b/test/unit/@node-red/util/lib/hooks_spec.js deleted file mode 100644 index 4b25f33d2..000000000 --- a/test/unit/@node-red/util/lib/hooks_spec.js +++ /dev/null @@ -1,338 +0,0 @@ -const should = require("should"); -const NR_TEST_UTILS = require("nr-test-utils"); - -const hooks = NR_TEST_UTILS.require("@node-red/util/lib/hooks"); - -describe("util/hooks", function() { - afterEach(function() { - hooks.clear(); - }) - it("allows a hook to be registered", function(done) { - let calledWith = null; - hooks.has("onSend").should.be.false(); - hooks.add("onSend", function(payload) { calledWith = payload } ) - hooks.has("onSend").should.be.true(); - let data = { a: 1 }; - hooks.trigger("onSend",data,err => { - calledWith.should.equal(data); - done(err); - }) - }) - it("rejects invalid hook id", function(done) { - try { - hooks.add("foo", function(payload) {}) - done(new Error("Invalid hook accepted")) - } catch(err) { - done(); - } - }) - it("calls hooks in the order they were registered", function(done) { - hooks.add("onSend", function(payload) { payload.order.push("A") } ) - hooks.add("onSend", function(payload) { payload.order.push("B") } ) - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - done(err); - }) - }) - - it("does not allow multiple hooks with same id.label", function() { - hooks.has("onSend.one").should.be.false(); - hooks.has("onSend").should.be.false(); - hooks.add("onSend.one", function(payload) { payload.order.push("A") } ); - hooks.has("onSend.one").should.be.true(); - hooks.has("onSend").should.be.true(); - (function() { - hooks.add("onSend.one", function(payload) { payload.order.push("B") } ) - }).should.throw(); - }) - - it("removes labelled hook", function(done) { - hooks.has("onSend.A").should.be.false(); - hooks.has("onSend.B").should.be.false(); - hooks.has("onSend").should.be.false(); - - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - - hooks.has("onSend.A").should.be.true(); - hooks.has("onSend.B").should.be.false(); - hooks.has("onSend").should.be.true(); - - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - hooks.has("onSend.A").should.be.true(); - hooks.has("onSend.B").should.be.true(); - hooks.has("onSend").should.be.true(); - - hooks.remove("onSend.A"); - - hooks.has("onSend.A").should.be.false(); - hooks.has("onSend.B").should.be.true(); - hooks.has("onSend").should.be.true(); - - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - try { - data.order.should.eql(["B"]) - - hooks.remove("onSend.B"); - - hooks.has("onSend.A").should.be.false(); - hooks.has("onSend.B").should.be.false(); - hooks.has("onSend").should.be.false(); - - done(err); - } catch(err2) { - done(err2); - } - }) - }) - - it("cannot remove unlabelled hook", function() { - hooks.add("onSend", function(payload) { payload.order.push("A") } ); - (function() { - hooks.remove("onSend") - }).should.throw(); - }) - it("removes all hooks with same label", function(done) { - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - hooks.add("preRoute.A", function(payload) { payload.order.push("C") } ) - hooks.add("preRoute.B", function(payload) { payload.order.push("D") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - hooks.trigger("preRoute", data, err => { - data.order.should.eql(["A","B","C","D"]) - - data.order = []; - - hooks.remove("*.A"); - - hooks.trigger("onSend",data,err => { - data.order.should.eql(["B"]) - hooks.trigger("preRoute", data, err => { - data.order.should.eql(["B","D"]) - }) - done(err); - }) - }) - }) - }) - it("allows a hook to remove itself whilst being called", function(done) { - let data = { order: [] } - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - hooks.add("onSend.B", function(payload) { - hooks.remove("*.B"); - }) - hooks.add("onSend.C", function(payload) { payload.order.push("C") } ) - hooks.add("onSend.D", function(payload) { payload.order.push("D") } ) - - hooks.trigger("onSend", data, err => { - try { - should.not.exist(err); - data.order.should.eql(["A","C","D"]) - done(); - } catch(e) { - done(e); - } - }) - }); - - it("allows a hook to remove itself and others whilst being called", function(done) { - let data = { order: [] } - hooks.add("onSend.A", function(payload) { payload.order.push("A") } ) - hooks.add("onSend.B", function(payload) { - hooks.remove("*.B"); - hooks.remove("*.C"); - }) - hooks.add("onSend.C", function(payload) { payload.order.push("C") } ) - hooks.add("onSend.D", function(payload) { payload.order.push("D") } ) - - hooks.trigger("onSend", data, err => { - try { - should.not.exist(err); - data.order.should.eql(["A","D"]) - done(); - } catch(e) { - done(e); - } - }) - }); - - it("halts execution on return false", function(done) { - hooks.add("onSend.A", function(payload) { payload.order.push("A"); return false } ) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - err.should.be.false(); - done(); - }) - }) - it("halts execution on thrown error", function(done) { - hooks.add("onSend.A", function(payload) { payload.order.push("A"); throw new Error("error") } ) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - should.exist(err); - err.should.not.be.false() - done(); - }) - }) - - it("handler can use callback function", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done() - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - done(err); - }) - }) - - it("handler can use callback function - halt execution", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done(false) - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - err.should.be.false() - done(); - }) - }) - it("handler can use callback function - halt on error", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - done(new Error("test error")) - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql([]) - should.exist(err); - err.should.not.be.false() - done(); - }) - }) - - it("handler be an async function", function(done) { - hooks.add("onSend.A", async function(payload) { - return new Promise(resolve => { - setTimeout(function() { - payload.order.push("A") - resolve() - },30) - }); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A","B"]) - done(err); - }) - }) - - it("handler be an async function - halt execution", function(done) { - hooks.add("onSend.A", async function(payload) { - return new Promise(resolve => { - setTimeout(function() { - payload.order.push("A") - resolve(false) - },30) - }); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql(["A"]) - done(err); - }) - }) - it("handler be an async function - halt on error", function(done) { - hooks.add("onSend.A", async function(payload) { - return new Promise((resolve,reject) => { - setTimeout(function() { - reject(new Error("test error")) - },30) - }); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data,err => { - data.order.should.eql([]) - should.exist(err); - err.should.not.be.false() - done(); - }) - }) - - - it("handler can use callback function - promise API", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done() - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data).then(() => { - data.order.should.eql(["A","B"]) - done() - }).catch(done) - }) - - it("handler can halt execution - promise API", function(done) { - hooks.add("onSend.A", function(payload, done) { - setTimeout(function() { - payload.order.push("A") - done(false) - },30) - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data).then(() => { - data.order.should.eql(["A"]) - done() - }).catch(done) - }) - - it("handler can halt execution on error - promise API", function(done) { - hooks.add("onSend.A", function(payload, done) { - throw new Error("error"); - }) - hooks.add("onSend.B", function(payload) { payload.order.push("B") } ) - - let data = { order:[] }; - hooks.trigger("onSend",data).then(() => { - done("hooks.trigger resolved unexpectedly") - }).catch(err => { - done(); - }) - }) -}); diff --git a/test/unit/@node-red/util/lib/i18n_spec.js b/test/unit/@node-red/util/lib/i18n_spec.js deleted file mode 100644 index 5deaba032..000000000 --- a/test/unit/@node-red/util/lib/i18n_spec.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - - - - var NR_TEST_UTILS = require("nr-test-utils"); - - var i18n = NR_TEST_UTILS.require("@node-red/util").i18n; - - -describe("@node-red/util/i18n", function() { - it.skip('more tests needed', function(){}) -}); diff --git a/test/unit/@node-red/util/lib/index_spec.js b/test/unit/@node-red/util/lib/index_spec.js deleted file mode 100644 index 3cf2347c1..000000000 --- a/test/unit/@node-red/util/lib/index_spec.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -describe("@node-red/util", function() { - it.skip('more tests needed', function(){}) -}); diff --git a/test/unit/@node-red/util/lib/log_spec.js b/test/unit/@node-red/util/lib/log_spec.js deleted file mode 100644 index 056f37672..000000000 --- a/test/unit/@node-red/util/lib/log_spec.js +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var util = require("util"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var log = NR_TEST_UTILS.require("@node-red/util").log; - - -describe("@node-red/util/log", function() { - beforeEach(function () { - var spy = sinon.stub(util, 'log').callsFake(function(arg){}); - var settings = {logging: { console: { level: 'metric', metrics: true } } }; - log.init(settings); - }); - - afterEach(function() { - util.log.restore(); - }); - - it('it can raise an error', function() { - var ret = log.error("This is an error"); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - }); - - it('it can raise a trace', function() { - var ret = log.trace("This is a trace"); - sinon.assert.calledWithMatch(util.log,"[trace] This is a trace"); - }); - - it('it can raise a debug', function() { - var ret = log.debug("This is a debug"); - sinon.assert.calledWithMatch(util.log,"[debug] This is a debug"); - }); - - it('it can raise a info', function() { - var ret = log.info("This is an info"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - }); - - it('it can raise a warn', function() { - var ret = log.warn("This is a warn"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - }); - - it('it can raise a metric', function() { - var metrics = {}; - metrics.level = log.METRIC; - metrics.nodeid = "testid"; - metrics.event = "node.test.testevent"; - metrics.msgid = "12345"; - metrics.value = "the metric payload"; - var ret = log.log(metrics); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[metric] ").should.equal(0); - var body = JSON.parse(util.log.firstCall.args[0].substring(9)); - body.should.have.a.property("nodeid","testid"); - body.should.have.a.property("event","node.test.testevent"); - body.should.have.a.property("msgid","12345"); - body.should.have.a.property("value","the metric payload"); - body.should.have.a.property("timestamp"); - body.should.have.a.property("level",log.METRIC); - }); - - it('it checks metrics are enabled', function() { - log.metric().should.equal(true); - var sett = {logging: { console: { level: 'info', metrics: false } } }; - log.init(sett); - log.metric().should.equal(false); - }); - - it('it logs node type and name if provided',function() { - log.log({level:log.INFO,type:"nodeType",msg:"test",name:"nodeName",id:"nodeId"}); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[nodeType:nodeName]").should.not.equal(-1); - }); - it('it logs node type and id if no name provided',function() { - log.log({level:log.INFO,type:"nodeType",msg:"test",id:"nodeId"}); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[nodeType:nodeId]").should.not.equal(-1); - }); - - it('ignores lower level messages and metrics', function() { - var settings = {logging: { console: { level: 'warn', metrics: false } } }; - log.init(settings); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.neverCalledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); - }); - it('ignores lower level messages but accepts metrics', function() { - var settings = {logging: { console: { level: 'log', metrics: true } } }; - log.init(settings); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.calledWithMatch(util.log,"[metric] "); - }); - - it('default settings set to INFO and metrics off', function() { - log.init({logging:{}}); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); - }); - it('no logger used if custom logger handler does not exist', function() { - var settings = {logging: { customLogger: { level: 'trace', metrics: true } } }; - log.init(settings); - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.neverCalledWithMatch(util.log,"[error] This is an error"); - sinon.assert.neverCalledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.neverCalledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); - }); - - it('add a custom log handler directly', function() { - var settings = {}; - log.init(settings); - - var logEvents = []; - var loggerOne = { - emit: function(event,msg) { - logEvents.push({logger:1,msg:msg}); - } - }; - var loggerTwo = { - emit: function(event,msg) { - logEvents.push({logger:2,msg:msg}); - } - }; - log.addHandler(loggerOne); - log.addHandler(loggerTwo); - - log.error("This is an error"); - log.warn("This is a warn"); - log.info("This is an info"); - log.debug("This is a debug"); - log.trace("This is a trace"); - log.log({level:log.METRIC,msg:"testMetric"}); - - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(6); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(6); - }); - - it('remove a custom log handler directly', function() { - var settings = {}; - log.init(settings); - - var logEvents = []; - var loggerOne = { - emit: function(event,msg) { - logEvents.push({logger:1,msg:msg}); - } - }; - var loggerTwo = { - emit: function(event,msg) { - logEvents.push({logger:2,msg:msg}); - } - }; - log.addHandler(loggerOne); - log.addHandler(loggerTwo); - - log.info("This is an info"); - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(1); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(1); - - - log.removeHandler(loggerTwo); - log.info("This is an info"); - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(2); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(1); - - log.removeHandler(loggerOne); - log.info("This is an info"); - logEvents.filter(function(evt) { return evt.logger === 1}).should.have.lengthOf(2); - logEvents.filter(function(evt) { return evt.logger === 2}).should.have.lengthOf(1); - - - }); - it('it can log without exception', function() { - var msg = { - msg: { - mystrangeobj:"hello", - }, - }; - msg.msg.toString = function(){ - throw new Error('Exception in toString - should have been caught'); - } - msg.msg.constructor = { name: "strangeobj" }; - var ret = log.info(msg.msg); - }); - it('it can log an object but use .message', function() { - var msg = { - msg: { - message: "my special message", - mystrangeobj:"hello", - }, - }; - var ret = log.info(msg.msg); - sinon.assert.calledWithMatch(util.log,"my special message"); - }); - - - -}); diff --git a/test/unit/@node-red/util/lib/util_spec.js b/test/unit/@node-red/util/lib/util_spec.js deleted file mode 100644 index 5007fe5c5..000000000 --- a/test/unit/@node-red/util/lib/util_spec.js +++ /dev/null @@ -1,1092 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); - -var NR_TEST_UTILS = require("nr-test-utils"); - -var util = NR_TEST_UTILS.require("@node-red/util").util; - -describe("@node-red/util/util", function() { - describe('generateId', function() { - it('generates an id', function() { - var id = util.generateId(); - var id2 = util.generateId(); - id.should.not.eql(id2); - }); - }); - describe('compareObjects', function() { - it('numbers', function() { - util.compareObjects(0,0).should.equal(true); - util.compareObjects(0,1).should.equal(false); - util.compareObjects(1000,1001).should.equal(false); - util.compareObjects(1000,1000).should.equal(true); - util.compareObjects(0,"0").should.equal(false); - util.compareObjects(1,"1").should.equal(false); - util.compareObjects(0,null).should.equal(false); - util.compareObjects(0,undefined).should.equal(false); - }); - it('strings', function() { - util.compareObjects("","").should.equal(true); - util.compareObjects("a","a").should.equal(true); - util.compareObjects("",null).should.equal(false); - util.compareObjects("",undefined).should.equal(false); - }); - - it('arrays', function() { - util.compareObjects(["a"],["a"]).should.equal(true); - util.compareObjects(["a"],["a","b"]).should.equal(false); - util.compareObjects(["a","b"],["b"]).should.equal(false); - util.compareObjects(["a"],"a").should.equal(false); - util.compareObjects([[1],["a"]],[[1],["a"]]).should.equal(true); - util.compareObjects([[1],["a"]],[["a"],[1]]).should.equal(false); - }); - it('objects', function() { - util.compareObjects({"a":1},{"a":1,"b":1}).should.equal(false); - util.compareObjects({"a":1,"b":1},{"a":1,"b":1}).should.equal(true); - util.compareObjects({"b":1,"a":1},{"a":1,"b":1}).should.equal(true); - }); - it('Buffer', function() { - util.compareObjects(Buffer.from("hello"),Buffer.from("hello")).should.equal(true); - util.compareObjects(Buffer.from("hello"),Buffer.from("hello ")).should.equal(false); - util.compareObjects(Buffer.from("hello"),"hello").should.equal(false); - }); - - }); - - describe('ensureString', function() { - it('strings are preserved', function() { - util.ensureString('string').should.equal('string'); - }); - it('Buffer is converted', function() { - var s = util.ensureString(Buffer.from('foo')); - s.should.equal('foo'); - (typeof s).should.equal('string'); - }); - it('Object is converted to JSON', function() { - var s = util.ensureString({foo: "bar"}); - (typeof s).should.equal('string'); - should.deepEqual(JSON.parse(s), {foo:"bar"}); - }); - it('stringifies other things', function() { - var s = util.ensureString(123); - (typeof s).should.equal('string'); - s.should.equal('123'); - }); - }); - - describe('ensureBuffer', function() { - it('Buffers are preserved', function() { - var b = Buffer.from(''); - util.ensureBuffer(b).should.equal(b); - }); - it('string is converted', function() { - var b = util.ensureBuffer('foo'); - var expected = Buffer.from('foo'); - for (var i = 0; i < expected.length; i++) { - b[i].should.equal(expected[i]); - } - Buffer.isBuffer(b).should.equal(true); - }); - it('Object is converted to JSON', function() { - var obj = {foo: "bar"} - var b = util.ensureBuffer(obj); - Buffer.isBuffer(b).should.equal(true); - should.deepEqual(JSON.parse(b), obj); - }); - it('stringifies other things', function() { - var b = util.ensureBuffer(123); - Buffer.isBuffer(b).should.equal(true); - var expected = Buffer.from('123'); - for (var i = 0; i < expected.length; i++) { - b[i].should.equal(expected[i]); - } - }); - }); - - describe('cloneMessage', function() { - it('clones a simple message', function() { - var msg = {string:"hi",array:[1,2,3],object:{a:1,subobject:{b:2}}}; - - var cloned = util.cloneMessage(msg); - - cloned.should.eql(msg); - - cloned.should.not.equal(msg); - cloned.array.should.not.equal(msg.string); - cloned.object.should.not.equal(msg.object); - cloned.object.subobject.should.not.equal(msg.object.subobject); - - cloned.should.not.have.property("req"); - cloned.should.not.have.property("res"); - }); - it('does not clone http req/res properties', function() { - var msg = {req:{a:1},res:{b:2}}; - - var cloned = util.cloneMessage(msg); - - cloned.should.eql(msg); - cloned.should.not.equal(msg); - - cloned.req.should.equal(msg.req); - cloned.res.should.equal(msg.res); - }); - it('handles undefined values without throwing an error', function() { - var result = util.cloneMessage(undefined); - should.not.exist(result); - }) - }); - describe('getObjectProperty', function() { - it('gets a property beginning with "msg."', function() { - // getMessageProperty strips off `msg.` prefixes. - // getObjectProperty does not - var obj = { msg: { a: "foo"}, a: "bar"}; - var v = util.getObjectProperty(obj,"msg.a"); - v.should.eql("foo"); - }) - }); - describe('getMessageProperty', function() { - it('retrieves a simple property', function() { - var v = util.getMessageProperty({a:"foo"},"msg.a"); - v.should.eql("foo"); - var v2 = util.getMessageProperty({a:"foo"},"a"); - v2.should.eql("foo"); - }); - it('retrieves a nested property', function() { - var v = util.getMessageProperty({a:"foo",b:{foo:1,bar:2}},"msg.b[msg.a]"); - v.should.eql(1); - var v2 = util.getMessageProperty({a:"bar",b:{foo:1,bar:2}},"b[msg.a]"); - v2.should.eql(2); - }); - - it('should return undefined if property does not exist', function() { - var v = util.getMessageProperty({a:"foo"},"msg.b"); - should.not.exist(v); - }); - it('should throw error if property parent does not exist', function() { - /*jshint immed: false */ - (function() { - util.getMessageProperty({a:"foo"},"msg.a.b.c"); - }).should.throw(); - }); - it('retrieves a property with array syntax', function() { - var v = util.getMessageProperty({a:["foo","bar"]},"msg.a[0]"); - v.should.eql("foo"); - var v2 = util.getMessageProperty({a:[null,{b:"foo"}]},"a[1].b"); - v2.should.eql("foo"); - var v3 = util.getMessageProperty({a:[[["foo"]]]},"a[0][0][0]"); - v3.should.eql("foo"); - }); - - }); - describe('setObjectProperty', function() { - it('set a property beginning with "msg."', function() { - // setMessageProperty strips off `msg.` prefixes. - // setObjectProperty does not - var obj = {}; - var result = util.setObjectProperty(obj,"msg.a","bar"); - result.should.be.true(); - obj.should.have.property("msg"); - obj.msg.should.have.property("a","bar"); - }) - }); - describe('setMessageProperty', function() { - it('sets a property', function() { - var msg = {a:"foo"}; - var result = util.setMessageProperty(msg,"msg.a","bar"); - result.should.be.true(); - msg.a.should.eql('bar'); - }); - it('sets a deep level property', function() { - var msg = {a:{b:{c:"foo"}}}; - var result = util.setMessageProperty(msg,"msg.a.b.c","bar"); - result.should.be.true(); - msg.a.b.c.should.eql('bar'); - }); - it('creates missing parent properties by default', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b.c","bar"); - result.should.be.true(); - msg.a.b.c.should.eql('bar'); - }) - it('does not create missing parent properties', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b.c","bar",false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not create missing parent properties of array', function () { - var msg = {a:{}}; - var result = util.setMessageProperty(msg, "msg.a.b[1].c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - it('does not create missing parent properties of string', function() { - var msg = {a:"foo"}; - var result = util.setMessageProperty(msg, "msg.a.b.c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not set property of existing string property', function() { - var msg = {a:"foo"}; - var result = util.setMessageProperty(msg, "msg.a.b", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - it('does not set property of existing number property', function() { - var msg = {a:123}; - var result = util.setMessageProperty(msg, "msg.a.b", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not create missing parent properties of number', function() { - var msg = {a:123}; - var result = util.setMessageProperty(msg, "msg.a.b.c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - it('does not set property of existing boolean property', function() { - var msg = {a:true}; - var result = util.setMessageProperty(msg, "msg.a.b", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - it('does not create missing parent properties of boolean', function() { - var msg = {a:true}; - var result = util.setMessageProperty(msg, "msg.a.b.c", "bar", false); - result.should.be.false(); - should.not.exist(msg.a.b); - }) - - - it('deletes property if value is undefined', function() { - var msg = {a:{b:{c:"foo"}}}; - var result = util.setMessageProperty(msg,"msg.a.b.c",undefined); - result.should.be.true(); - should.not.exist(msg.a.b.c); - }) - it('does not create missing parent properties if value is undefined', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b.c",undefined); - result.should.be.false(); - should.not.exist(msg.a.b); - }); - it('sets a property with array syntax', function() { - var msg = {a:{b:["foo",{c:["",""]}]}}; - var result = util.setMessageProperty(msg,"msg.a.b[1].c[1]","bar"); - result.should.be.true(); - msg.a.b[1].c[1].should.eql('bar'); - }); - it('creates missing array elements - final property', function() { - var msg = {a:[]}; - var result = util.setMessageProperty(msg,"msg.a[2]","bar"); - result.should.be.true(); - msg.a.should.have.length(3); - msg.a[2].should.eql("bar"); - }); - it('creates missing array elements - mid property', function() { - var msg = {}; - var result = util.setMessageProperty(msg,"msg.a[2].b","bar"); - result.should.be.true(); - msg.a.should.have.length(3); - msg.a[2].b.should.eql("bar"); - }); - it('creates missing array elements - multi-arrays', function() { - var msg = {}; - var result = util.setMessageProperty(msg,"msg.a[2][2]","bar"); - result.should.be.true(); - msg.a.should.have.length(3); - msg.a.should.be.instanceOf(Array); - msg.a[2].should.have.length(3); - msg.a[2].should.be.instanceOf(Array); - msg.a[2][2].should.eql("bar"); - }); - it('does not create missing array elements - mid property', function () { - var msg = {a:[]}; - var result = util.setMessageProperty(msg, "msg.a[1][1]", "bar", false); - result.should.be.false(); - msg.a.should.empty(); - }); - it('does not create missing array elements - final property', function() { - var msg = {a:{}}; - var result = util.setMessageProperty(msg,"msg.a.b[2]","bar",false); - result.should.be.false(); - should.not.exist(msg.a.b); - // check it has not been misinterpreted - msg.a.should.not.have.property("b[2]"); - }); - it('deletes property inside array if value is undefined', function() { - var msg = {a:[1,2,3]}; - var result = util.setMessageProperty(msg,"msg.a[1]",undefined); - result.should.be.true(); - msg.a.should.have.length(2); - msg.a[0].should.eql(1); - msg.a[1].should.eql(3); - }) - it('handles nested message property references', function() { - var obj = {a:"foo",b:{}}; - var result = util.setObjectProperty(obj,"b[msg.a]","bar"); - result.should.be.true(); - obj.b.should.have.property("foo","bar"); - }); - it('handles nested message property references', function() { - var obj = {a:"foo",b:{"foo":[0,0,0]}}; - var result = util.setObjectProperty(obj,"b[msg.a][2]","bar"); - result.should.be.true(); - obj.b.foo.should.eql([0,0,"bar"]) - }); - }); - - describe('evaluateNodeProperty', function() { - it('returns string',function() { - var result = util.evaluateNodeProperty('hello','str'); - result.should.eql('hello'); - }); - it('returns number',function() { - var result = util.evaluateNodeProperty('0123','num'); - result.should.eql(123); - }); - it('returns evaluated json',function() { - var result = util.evaluateNodeProperty('{"a":123}','json'); - result.should.eql({a:123}); - }); - it('returns regex',function() { - var result = util.evaluateNodeProperty('^abc$','re'); - result.toString().should.eql("/^abc$/"); - }); - it('returns boolean',function() { - var result = util.evaluateNodeProperty('true','bool'); - result.should.be.true(); - result = util.evaluateNodeProperty('TrUe','bool'); - result.should.be.true(); - result = util.evaluateNodeProperty('false','bool'); - result.should.be.false(); - result = util.evaluateNodeProperty('','bool'); - result.should.be.false(); - }); - it('returns date',function() { - var result = util.evaluateNodeProperty('','date'); - (Date.now() - result).should.be.approximately(0,50); - }); - it('returns bin', function () { - var result = util.evaluateNodeProperty('[1, 2]','bin'); - result[0].should.eql(1); - result[1].should.eql(2); - }); - it('returns msg property',function() { - var result = util.evaluateNodeProperty('foo.bar','msg',{},{foo:{bar:"123"}}); - result.should.eql("123"); - }); - it('throws an error if callback is not defined', function (done) { - try { - util.evaluateNodeProperty(' ','msg',{},{foo:{bar:"123"}}); - done("should throw an error"); - } catch (err) { - done(); - } - }); - it('returns flow property',function() { - var result = util.evaluateNodeProperty('foo.bar','flow',{ - context:function() { return { - flow: { get: function(k) { - if (k === 'foo.bar') { - return '123'; - } else { - return null; - } - }} - }} - },{}); - result.should.eql("123"); - }); - it('returns global property',function() { - var result = util.evaluateNodeProperty('foo.bar','global',{ - context:function() { return { - global: { get: function(k) { - if (k === 'foo.bar') { - return '123'; - } else { - return null; - } - }} - }} - },{}); - result.should.eql("123"); - }); - it('returns jsonata result', function () { - var result = util.evaluateNodeProperty('$abs(-1)','jsonata',{},{}); - result.should.eql(1); - }); - it('returns null', function() { - var result = util.evaluateNodeProperty(null,'null'); - (result === null).should.be.true(); - }) - describe('environment variable', function() { - before(function() { - process.env.NR_TEST_A = "foo"; - process.env.NR_TEST_B = "${NR_TEST_A}"; - }) - after(function() { - delete process.env.NR_TEST_A; - delete process.env.NR_TEST_B; - }) - - it('returns an environment variable - NR_TEST_A', function() { - var result = util.evaluateNodeProperty('NR_TEST_A','env'); - result.should.eql('foo'); - }); - it('returns an environment variable - ${NR_TEST_A}', function() { - var result = util.evaluateNodeProperty('${NR_TEST_A}','env'); - result.should.eql('foo'); - }); - it('returns an environment variable - ${NR_TEST_A', function() { - var result = util.evaluateNodeProperty('${NR_TEST_A','env'); - result.should.eql(''); - }); - it('returns an environment variable - foo${NR_TEST_A}bar', function() { - var result = util.evaluateNodeProperty('123${NR_TEST_A}456','env'); - result.should.eql('123foo456'); - }); - it('returns an environment variable - foo${NR_TEST_B}bar', function() { - var result = util.evaluateNodeProperty('123${NR_TEST_B}456','env'); - result.should.eql('123${NR_TEST_A}456'); - }); - - }); - }); - - describe('normalisePropertyExpression', function() { - function testABC(input,expected) { - var result = util.normalisePropertyExpression(input); - // console.log("+",input); - // console.log(result); - result.should.eql(expected); - } - function testABCWithMessage(input,msg,expected) { - var result = util.normalisePropertyExpression(input,msg); - // console.log("+",input); - // console.log(result); - result.should.eql(expected); - } - function testInvalid(input,msg) { - /*jshint immed: false */ - (function() { - util.normalisePropertyExpression(input,msg); - }).should.throw(); - } - function testToString(input,msg,expected) { - var result = util.normalisePropertyExpression(input,msg,true); - console.log("+",input); - console.log(result); - result.should.eql(expected); - } - it('pass a.b.c',function() { testABC('a.b.c',['a','b','c']); }) - it('pass a["b"]["c"]',function() { testABC('a["b"]["c"]',['a','b','c']); }) - it('pass a["b"].c',function() { testABC('a["b"].c',['a','b','c']); }) - it("pass a['b'].c",function() { testABC("a['b'].c",['a','b','c']); }) - - it("pass a[0].c",function() { testABC("a[0].c",['a',0,'c']); }) - it("pass a.0.c",function() { testABC("a.0.c",['a',0,'c']); }) - it("pass a['a.b[0]'].c",function() { testABC("a['a.b[0]'].c",['a','a.b[0]','c']); }) - it("pass a[0][0][0]",function() { testABC("a[0][0][0]",['a',0,0,0]); }) - it("pass '1.2.3.4'",function() { testABC("'1.2.3.4'",['1.2.3.4']); }) - it("pass 'a.b'[1]",function() { testABC("'a.b'[1]",['a.b',1]); }) - it("pass 'a.b'.c",function() { testABC("'a.b'.c",['a.b','c']); }) - - it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); }) - it("pass a[msg[msg.b]]",function() { testABC("a[msg[msg.b]]",["a",["msg",["msg","b"]]]); }) - it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); }) - it("pass a[msg.b]",function() { testABC("a[msg.b]",["a",["msg","b"]]); }) - it("pass a[msg['b]\"[']]",function() { testABC("a[msg['b]\"[']]",["a",["msg","b]\"["]]); }) - it("pass a[msg['b][']]",function() { testABC("a[msg['b][']]",["a",["msg","b]["]]); }) - it("pass b[msg.a][2]",function() { testABC("b[msg.a][2]",["b",["msg","a"],2])}) - - it("pass b[msg.a][2] (with message)",function() { testABCWithMessage("b[msg.a][2]",{a: "foo"},["b","foo",2])}) - - it('pass a.$b.c',function() { testABC('a.$b.c',['a','$b','c']); }) - it('pass a["$b"].c',function() { testABC('a["$b"].c',['a','$b','c']); }) - it('pass a._b.c',function() { testABC('a._b.c',['a','_b','c']); }) - it('pass a["_b"].c',function() { testABC('a["_b"].c',['a','_b','c']); }) - - it("pass a['a.b[0]'].c",function() { testToString("a['a.b[0]'].c",null,'a["a.b[0]"]["c"]'); }) - it("pass a.b.c",function() { testToString("a.b.c",null,'a["b"]["c"]'); }) - it('pass a[msg.c][0]["fred"]',function() { testToString('a[msg.c][0]["fred"]',{c:"123"},'a["123"][0]["fred"]'); }) - - it("fail a'b'.c",function() { testInvalid("a'b'.c"); }) - it("fail a['b'.c",function() { testInvalid("a['b'.c"); }) - it("fail a[]",function() { testInvalid("a[]"); }) - it("fail a]",function() { testInvalid("a]"); }) - it("fail a[",function() { testInvalid("a["); }) - it("fail a[0d]",function() { testInvalid("a[0d]"); }) - it("fail a['",function() { testInvalid("a['"); }) - it("fail a[']",function() { testInvalid("a[']"); }) - it("fail a[0']",function() { testInvalid("a[0']"); }) - it("fail a.[0]",function() { testInvalid("a.[0]"); }) - it("fail [0]",function() { testInvalid("[0]"); }) - it("fail a[0",function() { testInvalid("a[0"); }) - it("fail a.",function() { testInvalid("a."); }) - it("fail .a",function() { testInvalid(".a"); }) - it("fail a. b",function() { testInvalid("a. b"); }) - it("fail a.b",function() { testInvalid(" a.b"); }) - it("fail a[0].[1]",function() { testInvalid("a[0].[1]"); }) - it("fail a['']",function() { testInvalid("a['']"); }) - it("fail 'a.b'c",function() { testInvalid("'a.b'c"); }) - it("fail ",function() { testInvalid("");}) - it("fail a[b]",function() { testInvalid("a[b]"); }) - it("fail a[msg.]",function() { testInvalid("a[msg.]"); }) - it("fail a[msg[]",function() { testInvalid("a[msg[]"); }) - it("fail a[msg.[]]",function() { testInvalid("a[msg.[]]"); }) - it("fail a[msg['af]]",function() { testInvalid("a[msg['af]]"); }) - it("fail b[msg.undefined][2] (with message)",function() { testInvalid("b[msg.undefined][2]",{})}) - - }); - - describe('normaliseNodeTypeName', function() { - function normalise(input, expected) { - var result = util.normaliseNodeTypeName(input); - result.should.eql(expected); - } - - it('pass blank',function() { normalise("", "") }); - it('pass ab1',function() { normalise("ab1", "ab1") }); - it('pass AB1',function() { normalise("AB1", "aB1") }); - it('pass a b 1',function() { normalise("a b 1", "aB1") }); - it('pass a-b-1',function() { normalise("a-b-1", "aB1") }); - it('pass ab1 ',function() { normalise(" ab1 ", "ab1") }); - it('pass _a_b_1_',function() { normalise("_a_b_1_", "aB1") }); - it('pass http request',function() { normalise("http request", "httpRequest") }); - it('pass HttpRequest',function() { normalise("HttpRequest", "httpRequest") }); - }); - - describe('prepareJSONataExpression', function() { - it('prepares an expression', function() { - var result = util.prepareJSONataExpression('payload',{}); - result.should.have.property('evaluate'); - result.should.have.property('assign'); - result.should.have.property('_legacyMode', false); - }); - it('prepares a legacyMode expression', function() { - var result = util.prepareJSONataExpression('msg.payload',{}); - result.should.have.property('evaluate'); - result.should.have.property('assign'); - result.should.have.property('_legacyMode', true); - }); - }); - describe('evaluateJSONataExpression', function() { - it('evaluates an expression', function() { - var expr = util.prepareJSONataExpression('payload',{}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - result.should.eql("hello"); - }); - it('evaluates a legacyMode expression', function() { - var expr = util.prepareJSONataExpression('msg.payload',{}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - result.should.eql("hello"); - }); - it('accesses flow context from an expression', function() { - var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - result.should.eql("bar"); - }); - it('accesses undefined environment variable from an expression', function() { - var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql(''); - }); - it('accesses environment variable from an expression', function() { - process.env.UTIL_ENV = 'foo'; - var expr = util.prepareJSONataExpression('$env("UTIL_ENV")',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql('foo'); - }); - it('accesses moment from an expression', function() { - var expr = util.prepareJSONataExpression('$moment("2020-05-27", "YYYY-MM-DD").add(7, "days").add(1, "months").format("YYYY-MM-DD")',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql('2020-07-03'); - }); - it('accesses moment-timezone from an expression', function() { - var expr = util.prepareJSONataExpression('$moment("2013-11-18 11:55Z").tz("Asia/Taipei").format()',{}); - var result = util.evaluateJSONataExpression(expr,{}); - result.should.eql('2013-11-18T19:55:00+08:00'); - }); - it('handles non-existant flow context variable', function() { - var expr = util.prepareJSONataExpression('$flowContext("nonExistant")',{context:function() { return {flow:{get: function(key) { return {'foo':'bar'}[key]}}}}}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - should.not.exist(result); - }); - it('handles non-existant global context variable', function() { - var expr = util.prepareJSONataExpression('$globalContext("nonExistant")',{context:function() { return {global:{get: function(key) { return {'foo':'bar'}[key]}}}}}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"}); - should.not.exist(result); - }); - it('handles async flow context access', function(done) { - var expr = util.prepareJSONataExpression('$flowContext("foo")',{context:function() { return {flow:{get: function(key,store,callback) { setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('handles async global context access', function(done) { - var expr = util.prepareJSONataExpression('$globalContext("foo")',{context:function() { return {global:{get: function(key,store,callback) { setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('handles persistable store in flow context access', function(done) { - var storeName; - var expr = util.prepareJSONataExpression('$flowContext("foo", "flowStoreName")',{context:function() { return {flow:{get: function(key,store,callback) { storeName = store;setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - storeName.should.equal("flowStoreName"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('handles persistable store in global context access', function(done) { - var storeName; - var expr = util.prepareJSONataExpression('$globalContext("foo", "globalStoreName")',{context:function() { return {global:{get: function(key,store,callback) { storeName = store;setTimeout(()=>{callback(null,{'foo':'bar'}[key])},10)}}}}}); - util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value) { - try { - should.not.exist(err); - value.should.eql("bar"); - storeName.should.equal("globalStoreName"); - done(); - } catch(err2) { - done(err2); - } - }); - }) - it('callbacks with error when invalid expression was specified', function (done) { - var expr = util.prepareJSONataExpression('$abc(1)',{}); - var result = util.evaluateJSONataExpression(expr,{payload:"hello"},function(err,value){ - should.exist(err); - done(); - }); - }); - }); - - describe('encodeObject', function () { - it('encodes Error with message', function() { - var err = new Error("encode error"); - err.name = 'encodeError'; - var msg = {msg:err}; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var resultJson = JSON.parse(result.msg); - resultJson.name.should.eql('encodeError'); - resultJson.message.should.eql('encode error'); - }); - it('encodes Error without message', function() { - var err = new Error(); - err.name = 'encodeError'; - err.toString = function(){return 'error message';} - var msg = {msg:err}; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var resultJson = JSON.parse(result.msg); - resultJson.name.should.eql('encodeError'); - resultJson.message.should.eql('error message'); - }); - it('encodes Buffer', function() { - var msg = {msg:Buffer.from("abc")}; - var result = util.encodeObject(msg,{maxLength:4}); - result.format.should.eql("buffer[3]"); - result.msg[0].should.eql('6'); - result.msg[1].should.eql('1'); - result.msg[2].should.eql('6'); - result.msg[3].should.eql('2'); - }); - it('encodes function', function() { - var msg = {msg:function(){}}; - var result = util.encodeObject(msg); - result.format.should.eql("function"); - result.msg.should.eql('[function]'); - }); - it('encodes boolean', function() { - var msg = {msg:true}; - var result = util.encodeObject(msg); - result.format.should.eql("boolean"); - result.msg.should.eql('true'); - }); - it('encodes number', function() { - var msg = {msg:123}; - var result = util.encodeObject(msg); - result.format.should.eql("number"); - result.msg.should.eql('123'); - }); - it('encodes 0', function() { - var msg = {msg:0}; - var result = util.encodeObject(msg); - result.format.should.eql("number"); - result.msg.should.eql('0'); - }); - it('encodes null', function() { - var msg = {msg:null}; - var result = util.encodeObject(msg); - result.format.should.eql("null"); - result.msg.should.eql('(undefined)'); - }); - it('encodes undefined', function() { - var msg = {msg:undefined}; - var result = util.encodeObject(msg); - result.format.should.eql("undefined"); - result.msg.should.eql('(undefined)'); - }); - it('encodes string', function() { - var msg = {msg:'1234567890'}; - var result = util.encodeObject(msg,{maxLength:6}); - result.format.should.eql("string[10]"); - result.msg.should.eql('123456...'); - }); - - it('encodes Map', function() { - const m = new Map(); - m.set("a",1); - m.set("b",2); - var msg = {msg:m}; - var result = util.encodeObject(msg); - result.format.should.eql("map"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("__enc__",true); - resultJson.should.have.property("type","map"); - resultJson.should.have.property("data",{"a":1,"b":2}); - resultJson.should.have.property("length",2) - }); - - it('encodes Set', function() { - const m = new Set(); - m.add("a"); - m.add("b"); - var msg = {msg:m}; - var result = util.encodeObject(msg); - result.format.should.eql("set[2]"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("__enc__",true); - resultJson.should.have.property("type","set"); - resultJson.should.have.property("data",["a","b"]); - resultJson.should.have.property("length",2) - }); - - - describe('encode object', function() { - it('object', function() { - var msg = { msg:{"foo":"bar"} }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.foo.should.eql('bar'); - }); - it('object whose name includes error', function() { - function MyErrorObj(){ - this.name = 'my error obj'; - this.message = 'my error message'; - }; - var msg = { msg:new MyErrorObj() }; - var result = util.encodeObject(msg); - result.format.should.eql("MyErrorObj"); - var resultJson = JSON.parse(result.msg); - resultJson.name.should.eql('my error obj'); - resultJson.message.should.eql('my error message'); - }); - - it('object with undefined property', function() { - var msg = { msg:{a:1,b:undefined,c:3 } }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("a",1); - resultJson.should.have.property("c",3); - resultJson.should.have.property("b"); - resultJson.b.should.have.property("__enc__", true); - resultJson.b.should.have.property("type", "undefined"); - }); - it('object with Map property', function() { - const m = new Map(); - m.set("a",1); - m.set("b",2); - var msg = {msg:{"aMap":m}}; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("aMap"); - resultJson.aMap.should.have.property("__enc__",true); - resultJson.aMap.should.have.property("type","map"); - resultJson.aMap.should.have.property("data",{"a":1,"b":2}); - resultJson.aMap.should.have.property("length",2) - }); - it('object with Set property', function() { - const m = new Set(); - m.add("a"); - m.add("b"); - var msg = {msg:{"aSet":m}}; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.have.property("aSet"); - resultJson.aSet.should.have.property("__enc__",true); - resultJson.aSet.should.have.property("type","set"); - resultJson.aSet.should.have.property("data",["a","b"]); - resultJson.aSet.should.have.property("length",2) - }); - it('constructor of IncomingMessage', function() { - function IncomingMessage(){}; - var msg = { msg:new IncomingMessage() }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.empty(); - }); - it('_req key in msg', function() { - function Socket(){}; - var msg = { msg:{"_req":123} }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson._req.__enc__.should.eql(true); - resultJson._req.type.should.eql('internal'); - }); - it('_res key in msg', function() { - function Socket(){}; - var msg = { msg:{"_res":123} }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson._res.__enc__.should.eql(true); - resultJson._res.type.should.eql('internal'); - }); - it('array of error', function() { - var msg = { msg:[new Error("encode error")] }; - var result = util.encodeObject(msg); - result.format.should.eql("array[1]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].should.eql('Error: encode error'); - }); - it('long array in msg', function() { - var msg = {msg:{array:[1,2,3,4]}}; - var result = util.encodeObject(msg,{maxLength:2}); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.array.__enc__.should.eql(true); - resultJson.array.data[0].should.eql(1); - resultJson.array.data[1].should.eql(2); - resultJson.array.length.should.eql(4); - }); - it('array of string', function() { - var msg = { msg:["abcde","12345"] }; - var result = util.encodeObject(msg,{maxLength:3}); - result.format.should.eql("array[2]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].should.eql('abc...'); - resultJson[1].should.eql('123...'); - }); - it('array containing undefined', function() { - var msg = { msg:[1,undefined,3]}; - var result = util.encodeObject(msg); - result.format.should.eql("array[3]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].should.eql(1); - resultJson[2].should.eql(3); - resultJson[1].__enc__.should.be.true(); - resultJson[1].type.should.eql("undefined"); - }); - it('array of function', function() { - var msg = { msg:[function(){}] }; - var result = util.encodeObject(msg); - result.format.should.eql("array[1]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].__enc__.should.eql(true); - resultJson[0].type.should.eql('function'); - }); - it('array of number', function() { - var msg = { msg:[1,2,3] }; - var result = util.encodeObject(msg,{maxLength:2}); - result.format.should.eql("array[3]"); - var resultJson = JSON.parse(result.msg); - resultJson.__enc__.should.eql(true); - resultJson.data[0].should.eql(1); - resultJson.data[1].should.eql(2); - resultJson.data.length.should.eql(2); - resultJson.length.should.eql(3); - }); - it('array of special number', function() { - var msg = { msg:[NaN,Infinity,-Infinity] }; - var result = util.encodeObject(msg); - result.format.should.eql("array[3]"); - var resultJson = JSON.parse(result.msg); - resultJson[0].__enc__.should.eql(true); - resultJson[0].type.should.eql('number'); - resultJson[0].data.should.eql('NaN'); - resultJson[1].data.should.eql('Infinity'); - resultJson[2].data.should.eql('-Infinity'); - }); - it('constructor of Buffer in msg', function() { - var msg = { msg:{buffer:Buffer.from([1,2,3,4])} }; - var result = util.encodeObject(msg,{maxLength:2}); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.buffer.__enc__.should.eql(true); - resultJson.buffer.length.should.eql(4); - resultJson.buffer.data[0].should.eql(1); - resultJson.buffer.data[1].should.eql(2); - }); - it('constructor of ServerResponse', function() { - function ServerResponse(){}; - var msg = { msg: new ServerResponse() }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.should.eql('[internal]'); - }); - it('constructor of Socket in msg', function() { - function Socket(){}; - var msg = { msg: { socket: new Socket() } }; - var result = util.encodeObject(msg); - result.format.should.eql("Object"); - var resultJson = JSON.parse(result.msg); - resultJson.socket.should.eql('[internal]'); - }); - it('object which fails to serialise', function(done) { - var msg = { - msg: { - obj:{ - cantserialise:{ - message:'this will not be displayed', - toJSON: function(val) { - throw 'this exception should have been caught'; - return 'should not display because we threw first'; - }, - }, - canserialise:{ - message:'this should be displayed', - } - }, - } - }; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var success = (result.msg.indexOf('cantserialise') > 0); - success &= (result.msg.indexOf('this exception should have been caught') > 0); - success &= (result.msg.indexOf('canserialise') > 0); - success.should.eql(1); - done(); - }); - it('object which fails to serialise - different error type', function(done) { - var msg = { - msg: { - obj:{ - cantserialise:{ - message:'this will not be displayed', - toJSON: function(val) { - throw new Error('this exception should have been caught'); - return 'should not display because we threw first'; - }, - }, - canserialise:{ - message:'this should be displayed', - } - }, - } - }; - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var success = (result.msg.indexOf('cantserialise') > 0); - success &= (result.msg.indexOf('this exception should have been caught') > 0); - success &= (result.msg.indexOf('canserialise') > 0); - success.should.eql(1); - done(); - }); - it('very large object which fails to serialise should be truncated', function(done) { - var msg = { - msg: { - obj:{ - big:"", - cantserialise:{ - message:'this will not be displayed', - toJSON: function(val) { - throw new Error('this exception should have been caught'); - return 'should not display because we threw first'; - }, - }, - canserialise:{ - message:'this should be displayed', - } - }, - } - }; - - for (var i = 0; i < 1000; i++) { - msg.msg.obj.big += 'some more string '; - } - - var result = util.encodeObject(msg); - result.format.should.eql("error"); - var resultJson = JSON.parse(result.msg); - var success = (resultJson.message.length <= 1000); - success.should.eql(true); - done(); - }); - it('test bad toString', function(done) { - var msg = { - msg: { - mystrangeobj:"hello", - }, - }; - msg.msg.toString = function(){ - throw new Error('Exception in toString - should have been caught'); - } - msg.msg.constructor = { name: "strangeobj" }; - - var result = util.encodeObject(msg); - var success = (result.msg.indexOf('[Type not printable]') >= 0); - success.should.eql(true); - done(); - }); - it('test bad object constructor', function(done) { - var msg = { - msg: { - mystrangeobj:"hello", - constructor: { - get name(){ - throw new Error('Exception in constructor name'); - } - } - }, - }; - var result = util.encodeObject(msg); - done(); - }); - - }); - }); - -}); diff --git a/test/unit/_spec.js b/test/unit/_spec.js deleted file mode 100644 index d099836f3..000000000 --- a/test/unit/_spec.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -/** - * This test simply checks that for every .js file there exists - * a *_spec.js file under ./test correspondingly. - */ - -/** - * Currently we're only checking the core components under ./red - * TODO: Increase the scope of this check - */ - -var fs = require("fs-extra"); -var should = require("should"); -var path = require('path'); - -// Directories to check with .js files and _spec.js files respectively -var rootdir = path.resolve(__dirname, "../.."); -var jsdir = path.resolve(__dirname, "../../packages/node_modules/"); -var testdir = path.resolve(__dirname); - -var walkDirectory = function(dir) { - var p = fs.readdir(dir); - var errors = []; - return p.then(function(list) { - var promises = []; - list.forEach(function(file) { - var filePath = path.join(dir,file); - - if (!/@node-red\/(editor-client|nodes)/.test(filePath) && !/node-red\/settings\.js/.test(filePath) && !/\/docs\//.test(filePath)) { - promises.push(fs.stat(filePath).then(function(stat){ - if (stat.isDirectory()) { - return walkDirectory(filePath).then(function(results) { - if (results) { - errors = errors.concat(results); - } - }); - } else if (/\.js$/.test(filePath)) { - var testFile = filePath.replace(jsdir, testdir).replace(".js", "_spec.js"); - return fs.exists(testFile).then(function(exists) { - if (!exists) { - errors.push(testFile.substring(rootdir.length+1)); - } else { - return fs.stat(testFile).then(function(stat) { - if (stat.size === 0) { - errors.push("[empty] "+testFile.substring(rootdir.length+1)); - } - }) - } - }); - } - })); - } - }); - return Promise.all(promises).then(function() { - return errors; - }) - }); -} - -describe('_spec.js', function() { - this.timeout(50000); // we might not finish within the Mocha default timeout limit, project will also grow - it('is checking if all .js files have a corresponding _spec.js test file.', function(done) { - walkDirectory(jsdir).then(function(errors) { - if (errors.length > 0) { - var error = new Error("Missing/empty _spec files:\n\t"+errors.join("\n\t")); - done(error); - } else { - done(); - } - }); - }); -}); diff --git a/test/unit/node-red/lib/red_spec.js b/test/unit/node-red/lib/red_spec.js deleted file mode 100644 index 4cd430822..000000000 --- a/test/unit/node-red/lib/red_spec.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require("sinon"); -var fs = require("fs"); -var path = require("path"); - - -var NR_TEST_UTILS = require("nr-test-utils"); - -var api = NR_TEST_UTILS.require("@node-red/runtime/lib/api"); - -var RED = NR_TEST_UTILS.require("node-red"); - -var runtime = NR_TEST_UTILS.require("@node-red/runtime"); -var api = NR_TEST_UTILS.require("@node-red/runtime/lib/api"); - - -describe("red/red", function() { - - // describe("check build", function() { - // beforeEach(function() { - // sinon.stub(runtime,"init").callsFake(function() {}); - // sinon.stub(api,"init").callsFake(function() {}); - // // sinon.stub(RED,"version").callsFake(function() { return "version";}); - // }); - // afterEach(function() { - // runtime.init.restore(); - // api.init.restore(); - // fs.statSync.restore(); - // // RED.version.restore(); - // }); - // it.skip('warns if build has not been run',function() { - // sinon.stub(fs,"statSync").callsFake(function() { throw new Error();}); - // - // /*jshint immed: false */ - // (function() { - // RED.init({},{}); - // }).should.throw("Node-RED not built"); - // }); - // it('passed if build has been run',function() { - // sinon.stub(fs,"statSync").callsFake(function() { }); - // RED.init({},{}); - // }); - // }); - - describe("externals", function() { - it('reports version', function() { - /\d+\.\d+\.\d+(-git)?/.test(RED.version()).should.be.true(); - }); - it.skip('access server externals', function() { - // TODO: unstubable accessors - need to make this testable - // RED.app; - // RED.httpAdmin; - // RED.httpNode; - // RED.server; - }); - it.skip('only initialises api component if httpAdmin enabled'); - it.skip('stubs httpAdmin if httpAdmin disabled'); - it.skip('stubs httpNode if httpNode disabled'); - }); - -}); diff --git a/test/unit/node-red/red_spec.js b/test/unit/node-red/red_spec.js deleted file mode 100644 index ee46fc504..000000000 --- a/test/unit/node-red/red_spec.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var NR_TEST_UTILS = require("nr-test-utils"); - -describe("node-red/red", function() { - it.skip("NEEDS TESTS WRITING",function() {}); -}); From d63e5da60cb50e916fd79757d39d1132d71243ca Mon Sep 17 00:00:00 2001 From: "andrew.greene" Date: Wed, 8 Dec 2021 18:08:01 -0700 Subject: [PATCH 16/53] Add input/output to comment node --- .../@node-red/nodes/core/common/90-comment.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/common/90-comment.html b/packages/node_modules/@node-red/nodes/core/common/90-comment.html index cca7cd51d..579a92cc7 100644 --- a/packages/node_modules/@node-red/nodes/core/common/90-comment.html +++ b/packages/node_modules/@node-red/nodes/core/common/90-comment.html @@ -1,4 +1,4 @@ - + - - - diff --git a/packages/node_modules/@node-red/nodes/core/common/guide.html b/packages/node_modules/@node-red/nodes/core/common/guide.html new file mode 100644 index 000000000..dfff21e3e --- /dev/null +++ b/packages/node_modules/@node-red/nodes/core/common/guide.html @@ -0,0 +1,67 @@ + + + + + + diff --git a/packages/node_modules/@node-red/nodes/core/common/guide.js b/packages/node_modules/@node-red/nodes/core/common/guide.js new file mode 100644 index 000000000..44802d135 --- /dev/null +++ b/packages/node_modules/@node-red/nodes/core/common/guide.js @@ -0,0 +1,25 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +// 2022 Modification Copyright - Defense Unicorns + +module.exports = function(RED) { + "use strict"; + function GuideNode(n) { + RED.nodes.createNode(this,n); + } + RED.nodes.registerType("guide",GuideNode); +} diff --git a/packages/node_modules/@node-red/nodes/core/common/resource.html b/packages/node_modules/@node-red/nodes/core/common/resource.html new file mode 100644 index 000000000..d4db2f931 --- /dev/null +++ b/packages/node_modules/@node-red/nodes/core/common/resource.html @@ -0,0 +1,67 @@ + + + + + + diff --git a/packages/node_modules/@node-red/nodes/core/common/resource.js b/packages/node_modules/@node-red/nodes/core/common/resource.js new file mode 100644 index 000000000..180a600f2 --- /dev/null +++ b/packages/node_modules/@node-red/nodes/core/common/resource.js @@ -0,0 +1,25 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +// 2022 Modification Copyright - Defense Unicorns + +module.exports = function(RED) { + "use strict"; + function ResourceNode(n) { + RED.nodes.createNode(this,n); + } + RED.nodes.registerType("resource",ResourceNode); +} diff --git a/packages/node_modules/@node-red/nodes/core/common/task.html b/packages/node_modules/@node-red/nodes/core/common/task.html new file mode 100644 index 000000000..c1f8ebdac --- /dev/null +++ b/packages/node_modules/@node-red/nodes/core/common/task.html @@ -0,0 +1,67 @@ + + + + + + diff --git a/packages/node_modules/@node-red/nodes/core/common/task.js b/packages/node_modules/@node-red/nodes/core/common/task.js new file mode 100644 index 000000000..ce1c1410e --- /dev/null +++ b/packages/node_modules/@node-red/nodes/core/common/task.js @@ -0,0 +1,25 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +// 2022 Modification Copyright - Defense Unicorns + +module.exports = function(RED) { + "use strict"; + function TaskNode(n) { + RED.nodes.createNode(this,n); + } + RED.nodes.registerType("task",TaskNode); +} diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html deleted file mode 100644 index a1ed14f6d..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ /dev/null @@ -1,611 +0,0 @@ - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js deleted file mode 100644 index b84ee8571..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ /dev/null @@ -1,510 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - - var util = require("util"); - var vm = require("vm"); - var acorn = require("acorn"); - var acornWalk = require("acorn-walk"); - - function sendResults(node,send,_msgid,msgs,cloneFirstMessage) { - if (msgs == null) { - return; - } else if (!util.isArray(msgs)) { - msgs = [msgs]; - } - var msgCount = 0; - for (var m=0; m0) { - send(msgs); - } - } - - function createVMOpt(node, kind) { - var opt = { - filename: 'Function node'+kind+':'+node.id+(node.name?' ['+node.name+']':''), // filename for stack traces - displayErrors: true - // Using the following options causes node 4/6 to not include the line number - // in the stack output. So don't use them. - // lineOffset: -11, // line number offset to be used for stack traces - // columnOffset: 0, // column number offset to be used for stack traces - }; - return opt; - } - - function updateErrorInfo(err) { - if (err.stack) { - var stack = err.stack.toString(); - var m = /^([^:]+):([^:]+):(\d+).*/.exec(stack); - if (m) { - var line = parseInt(m[3]) -1; - var kind = "body:"; - if (/setup/.exec(m[1])) { - kind = "setup:"; - } - if (/cleanup/.exec(m[1])) { - kind = "cleanup:"; - } - err.message += " ("+kind+"line "+line+")"; - } - } - } - - function FunctionNode(n) { - RED.nodes.createNode(this,n); - var node = this; - node.name = n.name; - node.func = n.func; - node.outputs = n.outputs; - node.ini = n.initialize ? n.initialize.trim() : ""; - node.fin = n.finalize ? n.finalize.trim() : ""; - node.libs = n.libs || []; - - if (RED.settings.functionExternalModules === false && node.libs.length > 0) { - throw new Error(RED._("function.error.externalModuleNotAllowed")); - } - - - - var functionText = "var results = null;"+ - "results = (async function(msg,__send__,__done__){ "+ - "var __msgid__ = msg._msgid;"+ - "var node = {"+ - "id:__node__.id,"+ - "name:__node__.name,"+ - "path:__node__.path,"+ - "outputCount:__node__.outputCount,"+ - "log:__node__.log,"+ - "error:__node__.error,"+ - "warn:__node__.warn,"+ - "debug:__node__.debug,"+ - "trace:__node__.trace,"+ - "on:__node__.on,"+ - "status:__node__.status,"+ - "send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+ - "done:__done__"+ - "};\n"+ - node.func+"\n"+ - "})(msg,__send__,__done__);"; - - var handleNodeDoneCall = true; - - // Check to see if the Function appears to call `node.done()`. If so, - // we will assume it is well written and does actually call node.done(). - // Otherwise, we will call node.done() after the function returns regardless. - if (/node\.done\s*\(\s*\)/.test(functionText)) { - // We have spotted the code contains `node.done`. It could be in a comment - // so need to do the extra work to parse the AST and examine it properly. - acornWalk.simple(acorn.parse(functionText,{ecmaVersion: "latest"} ), { - CallExpression(astNode) { - if (astNode.callee && astNode.callee.object) { - if (astNode.callee.object.name === "node" && astNode.callee.property.name === "done") { - handleNodeDoneCall = false; - } - } - } - }) - } - - var finScript = null; - var finOpt = null; - node.topic = n.topic; - node.outstandingTimers = []; - node.outstandingIntervals = []; - node.clearStatus = false; - - var sandbox = { - console:console, - util:util, - Buffer:Buffer, - Date: Date, - RED: { - util: RED.util - }, - __node__: { - id: node.id, - name: node.name, - path: node._path, - outputCount: node.outputs, - log: function() { - node.log.apply(node, arguments); - }, - error: function() { - node.error.apply(node, arguments); - }, - warn: function() { - node.warn.apply(node, arguments); - }, - debug: function() { - node.debug.apply(node, arguments); - }, - trace: function() { - node.trace.apply(node, arguments); - }, - send: function(send, id, msgs, cloneMsg) { - sendResults(node, send, id, msgs, cloneMsg); - }, - on: function() { - if (arguments[0] === "input") { - throw new Error(RED._("function.error.inputListener")); - } - node.on.apply(node, arguments); - }, - status: function() { - node.clearStatus = true; - node.status.apply(node, arguments); - } - }, - context: { - set: function() { - node.context().set.apply(node,arguments); - }, - get: function() { - return node.context().get.apply(node,arguments); - }, - keys: function() { - return node.context().keys.apply(node,arguments); - }, - get global() { - return node.context().global; - }, - get flow() { - return node.context().flow; - } - }, - flow: { - set: function() { - node.context().flow.set.apply(node,arguments); - }, - get: function() { - return node.context().flow.get.apply(node,arguments); - }, - keys: function() { - return node.context().flow.keys.apply(node,arguments); - } - }, - global: { - set: function() { - node.context().global.set.apply(node,arguments); - }, - get: function() { - return node.context().global.get.apply(node,arguments); - }, - keys: function() { - return node.context().global.keys.apply(node,arguments); - } - }, - env: { - get: function(envVar) { - return RED.util.getSetting(node, envVar); - } - }, - setTimeout: function () { - var func = arguments[0]; - var timerId; - arguments[0] = function() { - sandbox.clearTimeout(timerId); - try { - func.apply(node,arguments); - } catch(err) { - node.error(err,{}); - } - }; - timerId = setTimeout.apply(node,arguments); - node.outstandingTimers.push(timerId); - return timerId; - }, - clearTimeout: function(id) { - clearTimeout(id); - var index = node.outstandingTimers.indexOf(id); - if (index > -1) { - node.outstandingTimers.splice(index,1); - } - }, - setInterval: function() { - var func = arguments[0]; - var timerId; - arguments[0] = function() { - try { - func.apply(node,arguments); - } catch(err) { - node.error(err,{}); - } - }; - timerId = setInterval.apply(node,arguments); - node.outstandingIntervals.push(timerId); - return timerId; - }, - clearInterval: function(id) { - clearInterval(id); - var index = node.outstandingIntervals.indexOf(id); - if (index > -1) { - node.outstandingIntervals.splice(index,1); - } - } - }; - if (util.hasOwnProperty('promisify')) { - sandbox.setTimeout[util.promisify.custom] = function(after, value) { - return new Promise(function(resolve, reject) { - sandbox.setTimeout(function(){ resolve(value); }, after); - }); - }; - sandbox.promisify = util.promisify; - } - const moduleLoadPromises = []; - - if (node.hasOwnProperty("libs")) { - let moduleErrors = false; - var modules = node.libs; - modules.forEach(module => { - var vname = module.hasOwnProperty("var") ? module.var : null; - if (vname && (vname !== "")) { - if (sandbox.hasOwnProperty(vname) || vname === 'node') { - node.error(RED._("function.error.moduleNameError",{name:vname})) - moduleErrors = true; - return; - } - sandbox[vname] = null; - var spec = module.module; - if (spec && (spec !== "")) { - moduleLoadPromises.push(RED.import(module.module).then(lib => { - sandbox[vname] = lib.default; - }).catch(err => { - node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()})) - throw err; - })); - } - } - }); - if (moduleErrors) { - throw new Error(RED._("function.error.externalModuleLoadError")); - } - } - const RESOLVING = 0; - const RESOLVED = 1; - const ERROR = 2; - var state = RESOLVING; - var messages = []; - var processMessage = (() => {}); - - node.on("input", function(msg,send,done) { - if(state === RESOLVING) { - messages.push({msg:msg, send:send, done:done}); - } - else if(state === RESOLVED) { - processMessage(msg, send, done); - } - }); - Promise.all(moduleLoadPromises).then(() => { - var context = vm.createContext(sandbox); - try { - var iniScript = null; - var iniOpt = null; - if (node.ini && (node.ini !== "")) { - var iniText = ` - (async function(__send__) { - var node = { - id:__node__.id, - name:__node__.name, - path:__node__.path, - outputCount:__node__.outputCount, - log:__node__.log, - error:__node__.error, - warn:__node__.warn, - debug:__node__.debug, - trace:__node__.trace, - status:__node__.status, - send: function(msgs, cloneMsg) { - __node__.send(__send__, RED.util.generateId(), msgs, cloneMsg); - } - }; - `+ node.ini +` - })(__initSend__);`; - iniOpt = createVMOpt(node, " setup"); - iniScript = new vm.Script(iniText, iniOpt); - } - node.script = vm.createScript(functionText, createVMOpt(node, "")); - if (node.fin && (node.fin !== "")) { - var finText = `(function () { - var node = { - id:__node__.id, - name:__node__.name, - path:__node__.path, - outputCount:__node__.outputCount, - log:__node__.log, - error:__node__.error, - warn:__node__.warn, - debug:__node__.debug, - trace:__node__.trace, - status:__node__.status, - send: function(msgs, cloneMsg) { - __node__.error("Cannot send from close function"); - } - }; - `+node.fin +` - })();`; - finOpt = createVMOpt(node, " cleanup"); - finScript = new vm.Script(finText, finOpt); - } - var promise = Promise.resolve(); - if (iniScript) { - context.__initSend__ = function(msgs) { node.send(msgs); }; - promise = iniScript.runInContext(context, iniOpt); - } - - processMessage = function (msg, send, done) { - var start = process.hrtime(); - context.msg = msg; - context.__send__ = send; - context.__done__ = done; - - node.script.runInContext(context); - context.results.then(function(results) { - sendResults(node,send,msg._msgid,results,false); - if (handleNodeDoneCall) { - done(); - } - - var duration = process.hrtime(start); - var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100; - node.metric("duration", msg, converted); - if (process.env.NODE_RED_FUNCTION_TIME) { - node.status({fill:"yellow",shape:"dot",text:""+converted}); - } - }).catch(err => { - if ((typeof err === "object") && err.hasOwnProperty("stack")) { - //remove unwanted part - var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/); - err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n'); - var stack = err.stack.split(/\r?\n/); - - //store the error in msg to be used in flows - msg.error = err; - - var line = 0; - var errorMessage; - if (stack.length > 0) { - while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) { - line++; - } - - if (line < stack.length) { - errorMessage = stack[line]; - var m = /:(\d+):(\d+)$/.exec(stack[line+1]); - if (m) { - var lineno = Number(m[1])-1; - var cha = m[2]; - errorMessage += " (line "+lineno+", col "+cha+")"; - } - } - } - if (!errorMessage) { - errorMessage = err.toString(); - } - done(errorMessage); - } - else if (typeof err === "string") { - done(err); - } - else { - done(JSON.stringify(err)); - } - }); - } - - node.on("close", function() { - if (finScript) { - try { - finScript.runInContext(context, finOpt); - } - catch (err) { - node.error(err); - } - } - while (node.outstandingTimers.length > 0) { - clearTimeout(node.outstandingTimers.pop()); - } - while (node.outstandingIntervals.length > 0) { - clearInterval(node.outstandingIntervals.pop()); - } - if (node.clearStatus) { - node.status({}); - } - }); - - promise.then(function (v) { - var msgs = messages; - messages = []; - while (msgs.length > 0) { - msgs.forEach(function (s) { - processMessage(s.msg, s.send, s.done); - }); - msgs = messages; - messages = []; - } - state = RESOLVED; - }).catch((error) => { - messages = []; - state = ERROR; - node.error(error); - }); - - } - catch(err) { - // eg SyntaxError - which v8 doesn't include line number information - // so we can't do better than this - updateErrorInfo(err); - node.error(err); - } - }).catch(err => { - node.error(RED._("function.error.externalModuleLoadError")); - }); - } - RED.nodes.registerType("function",FunctionNode, { - dynamicModuleList: "libs", - settings: { - functionExternalModules: { value: true, exportable: true } - } - }); - RED.library.register("functions"); -}; diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.html b/packages/node_modules/@node-red/nodes/core/function/89-delay.html deleted file mode 100644 index 8ee404924..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/function/90-exec.js b/packages/node_modules/@node-red/nodes/core/function/90-exec.js deleted file mode 100644 index 2ae9947dd..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/90-exec.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var spawn = require('child_process').spawn; - var exec = require('child_process').exec; - var fs = require('fs'); - var isUtf8 = require('is-utf8'); - - function ExecNode(n) { - RED.nodes.createNode(this,n); - this.cmd = (n.command || "").trim(); - if (n.addpay === undefined) { n.addpay = true; } - this.addpay = n.addpay; - if (this.addpay === true) { - this.addpay = "payload"; - } - this.append = (n.append || "").trim(); - this.useSpawn = (n.useSpawn == "true"); - this.timer = Number(n.timer || 0)*1000; - this.activeProcesses = {}; - this.oldrc = (n.oldrc || false).toString(); - this.execOpt = {encoding:'binary', maxBuffer:RED.settings.execMaxBufferSize||10000000, windowsHide: (n.winHide === true)}; - this.spawnOpt = {windowsHide: (n.winHide === true) } - var node = this; - - if (process.platform === 'linux' && fs.existsSync('/bin/bash')) { node.execOpt.shell = '/bin/bash'; } - - var cleanup = function(p) { - node.activeProcesses[p].kill(); - //node.status({fill:"red",shape:"dot",text:"timeout"}); - //node.error("Exec node timeout"); - } - - this.on("input", function(msg, nodeSend, nodeDone) { - if (msg.hasOwnProperty("kill")) { - if (typeof msg.kill !== "string" || msg.kill.length === 0 || !msg.kill.toUpperCase().startsWith("SIG") ) { msg.kill = "SIGTERM"; } - if (msg.hasOwnProperty("pid")) { - if (node.activeProcesses.hasOwnProperty(msg.pid) ) { - node.activeProcesses[msg.pid].kill(msg.kill.toUpperCase()); - node.status({fill:"red",shape:"dot",text:"killed"}); - } - } - else { - if (Object.keys(node.activeProcesses).length === 1) { - node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill(msg.kill.toUpperCase()); - node.status({fill:"red",shape:"dot",text:"killed"}); - } - } - nodeDone(); - } - else { - var child; - // make the extra args into an array - // then prepend with the msg.payload - var arg = node.cmd; - if (node.addpay) { - var value = RED.util.getMessageProperty(msg, node.addpay); - if (value !== undefined) { - arg += " " + value; - } - } - if (node.append.trim() !== "") { arg += " " + node.append; } - if (this.useSpawn === true) { - // slice whole line by spaces and removes any quotes since spawn can't handle them - arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g).map((a) => { - if (/^".*"$/.test(a)) { - return a.slice(1,-1) - } else { - return a - } - }); - var cmd = arg.shift(); - /* istanbul ignore else */ - node.debug(cmd+" ["+arg+"]"); - child = spawn(cmd,arg,node.spawnOpt); - node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid}); - var unknownCommand = (child.pid === undefined); - if (node.timer !== 0) { - child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer); - } - node.activeProcesses[child.pid] = child; - child.stdout.on('data', function (data) { - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - // console.log('[exec] stdout: ' + data,child.pid); - if (isUtf8(data)) { msg.payload = data.toString(); } - else { msg.payload = data; } - nodeSend([RED.util.cloneMessage(msg),null,null]); - } - }); - child.stderr.on('data', function (data) { - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - if (isUtf8(data)) { msg.payload = data.toString(); } - else { msg.payload = Buffer.from(data); } - nodeSend([null,RED.util.cloneMessage(msg),null]); - } - }); - child.on('close', function (code,signal) { - if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) { - delete node.activeProcesses[child.pid]; - if (child.tout) { clearTimeout(child.tout); } - msg.payload = code; - if (node.oldrc === "false") { - msg.payload = {code:code}; - if (signal) { msg.payload.signal = signal; } - } - if (code === 0) { node.status({}); } - if (code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } - else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc:"+code}); } - else { node.status({fill:"yellow",shape:"dot",text:"rc:"+code}); } - nodeSend([null,null,RED.util.cloneMessage(msg)]); - } - nodeDone(); - }); - child.on('error', function (code) { - if (child.tout) { clearTimeout(child.tout); } - delete node.activeProcesses[child.pid]; - if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) { - node.error(code,RED.util.cloneMessage(msg)); - } - }); - } - else { - /* istanbul ignore else */ - node.debug(arg); - child = exec(arg, node.execOpt, function (error, stdout, stderr) { - var msg2, msg3; - delete msg.payload; - if (stderr) { - msg2 = RED.util.cloneMessage(msg); - msg2.payload = stderr; - } - msg.payload = Buffer.from(stdout,"binary"); - if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); } - node.status({}); - //console.log('[exec] stdout: ' + stdout); - //console.log('[exec] stderr: ' + stderr); - if (error !== null) { - msg3 = RED.util.cloneMessage(msg); - msg3.payload = {code:error.code, message:error.message}; - if (error.signal) { msg3.payload.signal = error.signal; } - if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } - else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); } - node.debug('error:' + error); - } - else if (node.oldrc === "false") { - msg3 = RED.util.cloneMessage(msg); - msg3.payload = {code:0}; - } - if (!msg3) { node.status({}); } - else { - msg.rc = msg3.payload; - if (msg2) { msg2.rc = msg3.payload; } - } - nodeSend([msg,msg2,msg3]); - if (child.tout) { clearTimeout(child.tout); } - delete node.activeProcesses[child.pid]; - nodeDone(); - }); - node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid}); - child.on('error',function() {}); - if (node.timer !== 0) { - child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer); - } - node.activeProcesses[child.pid] = child; - } - } - }); - - this.on('close',function() { - for (var pid in node.activeProcesses) { - /* istanbul ignore else */ - if (node.activeProcesses.hasOwnProperty(pid)) { - if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); } - // console.log("KILLING",pid); - var process = node.activeProcesses[pid]; - node.activeProcesses[pid] = null; - process.kill(); - } - } - node.activeProcesses = {}; - node.status({}); - }); - } - RED.nodes.registerType("exec",ExecNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/function/rbe.js b/packages/node_modules/@node-red/nodes/core/function/rbe.js deleted file mode 100644 index eb526a441..000000000 --- a/packages/node_modules/@node-red/nodes/core/function/rbe.js +++ /dev/null @@ -1,97 +0,0 @@ - -module.exports = function(RED) { - "use strict"; - function RbeNode(n) { - RED.nodes.createNode(this,n); - this.func = n.func || "rbe"; - this.gap = n.gap || "0"; - this.start = n.start || ''; - this.inout = n.inout || "out"; - this.pc = false; - if (this.gap.substr(-1) === "%") { - this.pc = true; - this.gap = parseFloat(this.gap); - } - this.g = this.gap; - this.property = n.property || "payload"; - this.topi = n.topi || "topic"; - this.septopics = true; - if (n.septopics !== undefined && n.septopics === false) { - this.septopics = false; - } - - var node = this; - - node.previous = {}; - this.on("input",function(msg) { - var topic; - try { - topic = RED.util.getMessageProperty(msg,node.topi); - } - catch(e) { } - if (msg.hasOwnProperty("reset")) { - if (node.septopics && topic && (typeof topic === "string") && (topic !== "")) { - delete node.previous[msg.topic]; - } - else { node.previous = {}; } - } - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - var t = "_no_topic"; - if (node.septopics) { t = topic || t; } - if ((this.func === "rbe") || (this.func === "rbei")) { - var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false; - if (typeof(value) === "object") { - if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; } - if (!RED.util.compareObjects(value, node.previous[t])) { - node.previous[t] = RED.util.cloneMessage(value); - if (doSend) { node.send(msg); } - } - } - else { - if (value !== node.previous[t]) { - node.previous[t] = RED.util.cloneMessage(value); - if (doSend) { node.send(msg); } - } - } - } - else { - var n = parseFloat(value); - if (!isNaN(n)) { - if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband" || this.func === "narrowbandEq")) { - if (node.start === '') { node.previous[t] = n; } - else { node.previous[t] = node.start; } - } - if (node.pc) { node.gap = Math.abs(node.previous[t] * node.g / 100) || 0; } - else { node.gap = Number(node.gap); } - if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; } - if (node.previous[t] === undefined) { node.previous[t] = n - node.gap - 1; } - if (Math.abs(n - node.previous[t]) === node.gap) { - if ((this.func === "deadbandEq")||(this.func === "narrowband")) { - if (node.inout === "out") { node.previous[t] = n; } - node.send(msg); - } - } - else if (Math.abs(n - node.previous[t]) > node.gap) { - if (this.func === "deadband" || this.func === "deadbandEq") { - if (node.inout === "out") { node.previous[t] = n; } - node.send(msg); - } - } - else if (Math.abs(n - node.previous[t]) < node.gap) { - if ((this.func === "narrowband")||(this.func === "narrowbandEq")) { - if (node.inout === "out") { node.previous[t] = n; } - node.send(msg); - } - } - if (node.inout === "in") { node.previous[t] = n; } - } - else { - node.warn(RED._("rbe.warn.nonumber")); - } - } - } // ignore msg with no payload property. - }); - } - RED.nodes.registerType("rbe",RbeNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js deleted file mode 100644 index bf677b490..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js +++ /dev/null @@ -1,675 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - const got = require("got"); - const {CookieJar} = require("tough-cookie"); - const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent'); - const FormData = require('form-data'); - const { v4: uuid } = require('uuid'); - const crypto = require('crypto'); - const URL = require("url").URL - var mustache = require("mustache"); - var querystring = require("querystring"); - var cookie = require("cookie"); - var hashSum = require("hash-sum"); - - - // Cache a reference to the existing https.request function - // so we can compare later to see if an old agent-base instance - // has been required. - // This is generally okay as the core nodes are required before - // any contrib nodes. Where it will fail is if the agent-base module - // is required via the settings file or outside of Node-RED before it - // is started. - // If there are other modules that patch the function, they will get undone - // as well. Not much we can do about that right now. Patching core - // functions is bad. - const HTTPS_MODULE = require("https"); - const HTTPS_REQUEST = HTTPS_MODULE.request; - - function checkNodeAgentPatch() { - if (HTTPS_MODULE.request !== HTTPS_REQUEST && HTTPS_MODULE.request.length === 2) { - RED.log.warn(` - ---------------------------------------------------------------------- -Patched https.request function detected. This will break the -HTTP Request node. The original code has now been restored. - -This is likely caused by a contrib node including an old version of -the 'agent-base@<5.0.0' module. - -You can identify what node is at fault by running: - npm list agent-base -in your Node-RED user directory (${RED.settings.userDir}). ---------------------------------------------------------------------- -`); - HTTPS_MODULE.request = HTTPS_REQUEST - } - } - - function HTTPRequest(n) { - RED.nodes.createNode(this,n); - checkNodeAgentPatch(); - var node = this; - var nodeUrl = n.url; - var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; - var nodeMethod = n.method || "GET"; - var paytoqs = false; - var paytobody = false; - var redirectList = []; - var sendErrorsToCatch = n.senderr; - - var nodeHTTPPersistent = n["persist"]; - if (n.tls) { - var tlsNode = RED.nodes.getNode(n.tls); - } - this.ret = n.ret || "txt"; - this.authType = n.authType || "basic"; - if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; } - else { this.reqTimeout = 120000; } - - if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; } - else if (n.paytoqs === "body") { paytobody = true; } - - - var prox, noprox; - if (process.env.http_proxy) { prox = process.env.http_proxy; } - if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } - if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } - if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } - - var proxyConfig = null; - if (n.proxy) { - proxyConfig = RED.nodes.getNode(n.proxy); - prox = proxyConfig.url; - noprox = proxyConfig.noproxy; - } - - let timingLog = false; - if (RED.settings.hasOwnProperty("httpRequestTimingLog")) { - timingLog = RED.settings.httpRequestTimingLog; - } - - this.on("input",function(msg,nodeSend,nodeDone) { - checkNodeAgentPatch(); - //reset redirectList on each request - redirectList = []; - var preRequestTimestamp = process.hrtime(); - node.status({fill:"blue",shape:"dot",text:"httpin.status.requesting"}); - var url = nodeUrl || msg.url; - if (msg.url && nodeUrl && (nodeUrl !== msg.url)) { // revert change below when warning is finally removed - node.warn(RED._("common.errors.nooverride")); - } - - if (isTemplatedUrl) { - url = mustache.render(nodeUrl,msg); - } - if (!url) { - node.error(RED._("httpin.errors.no-url"),msg); - nodeDone(); - return; - } - - - // url must start http:// or https:// so assume http:// if not set - if (url.indexOf("://") !== -1 && url.indexOf("http") !== 0) { - node.warn(RED._("httpin.errors.invalid-transport")); - node.status({fill:"red",shape:"ring",text:"httpin.errors.invalid-transport"}); - nodeDone(); - return; - } - if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) { - if (tlsNode) { - url = "https://"+url; - } else { - url = "http://"+url; - } - } - - // The Request module used in Node-RED 1.x was tolerant of query strings that - // were partially encoded. For example - "?a=hello%20there&b=20%" - // The GOT module doesn't like that. - // The following is an attempt to normalise the url to ensure it is properly - // encoded. We cannot just encode it directly as we don't want any valid - // encoded entity to end up doubly encoded. - if (url.indexOf("?") > -1) { - // Only do this if there is a query string to deal with - const [hostPath, ...queryString] = url.split("?") - const query = queryString.join("?"); - if (query) { - // Look for any instance of % not followed by two hex chars. - // Replace any we find with %25. - const escapedQueryString = query.replace(/(%.?.?)/g, function(v) { - if (/^%[a-f0-9]{2}/i.test(v)) { - return v; - } - return v.replace(/%/,"%25") - }) - url = hostPath+"?"+escapedQueryString; - } - } - - var method = nodeMethod.toUpperCase() || "GET"; - if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set - node.warn(RED._("common.errors.nooverride")); - } - if (msg.method && n.method && (n.method === "use")) { - method = msg.method.toUpperCase(); // use the msg parameter - } - - // var isHttps = (/^https/i.test(url)); - - var opts = {}; - // set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(. - // Had to remove this to get http->https redirect to work - // opts.defaultPort = isHttps?443:80; - opts.timeout = node.reqTimeout; - opts.throwHttpErrors = false; - // TODO: add UI option to auto decompress. Setting to false for 1.x compatibility - opts.decompress = false; - opts.method = method; - opts.headers = {}; - opts.retry = 0; - opts.responseType = 'buffer'; - opts.maxRedirects = 21; - opts.cookieJar = new CookieJar(); - opts.ignoreInvalidCookies = true; - opts.forever = nodeHTTPPersistent; - if (msg.requestTimeout !== undefined) { - if (isNaN(msg.requestTimeout)) { - node.warn(RED._("httpin.errors.timeout-isnan")); - } else if (msg.requestTimeout < 1) { - node.warn(RED._("httpin.errors.timeout-isnegative")); - } else { - opts.timeout = msg.requestTimeout; - } - } - const originalHeaderMap = {}; - - opts.hooks = { - beforeRequest: [ - options => { - // Whilst HTTP headers are meant to be case-insensitive, - // in the real world, there are servers that aren't so compliant. - // GOT will lower case all headers given a chance, so we need - // to restore the case of any headers the user has set. - Object.keys(options.headers).forEach(h => { - if (originalHeaderMap[h] && originalHeaderMap[h] !== h) { - options.headers[originalHeaderMap[h]] = options.headers[h]; - delete options.headers[h]; - } - }) - } - ], - beforeRedirect: [ - (options, response) => { - let redirectInfo = { - location: response.headers.location - } - if (response.headers.hasOwnProperty('set-cookie')) { - redirectInfo.cookies = extractCookies(response.headers['set-cookie']); - } - redirectList.push(redirectInfo) - } - ] - } - - var ctSet = "Content-Type"; // set default camel case - var clSet = "Content-Length"; - if (msg.headers) { - if (msg.headers.hasOwnProperty('x-node-red-request-node')) { - var headerHash = msg.headers['x-node-red-request-node']; - delete msg.headers['x-node-red-request-node']; - var hash = hashSum(msg.headers); - if (hash === headerHash) { - delete msg.headers; - } - } - if (msg.headers) { - for (var v in msg.headers) { - if (msg.headers.hasOwnProperty(v)) { - var name = v.toLowerCase(); - if (name !== "content-type" && name !== "content-length") { - // only normalise the known headers used later in this - // function. Otherwise leave them alone. - name = v; - } - else if (name === 'content-type') { ctSet = v; } - else { clSet = v; } - opts.headers[name] = msg.headers[v]; - } - } - } - } - - if (msg.hasOwnProperty('followRedirects')) { - opts.followRedirect = !!msg.followRedirects; - } - - if (opts.headers.hasOwnProperty('cookie')) { - var cookies = cookie.parse(opts.headers.cookie, {decode:String}); - for (var name in cookies) { - opts.cookieJar.setCookieSync(cookie.serialize(name, cookies[name], {encode:String}), url, {ignoreError: true}); - } - delete opts.headers.cookie; - } - if (msg.cookies) { - for (var name in msg.cookies) { - if (msg.cookies.hasOwnProperty(name)) { - if (msg.cookies[name] === null || msg.cookies[name].value === null) { - // This case clears a cookie for HTTP In/Response nodes. - // Ignore for this node. - } else if (typeof msg.cookies[name] === 'object') { - if(msg.cookies[name].encode === false){ - // If the encode option is false, the value is not encoded. - opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name].value, {encode: String}), url, {ignoreError: true}); - } else { - // The value is encoded by encodeURIComponent(). - opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name].value), url, {ignoreError: true}); - } - } else { - opts.cookieJar.setCookieSync(cookie.serialize(name, msg.cookies[name]), url, {ignoreError: true}); - } - } - } - } - var parsedURL = new URL(url) - this.credentials = this.credentials || {} - if (parsedURL.username && !this.credentials.user) { - this.credentials.user = parsedURL.username - } - if (parsedURL.password && !this.credentials.password) { - this.credentials.password = parsedURL.password - } - if (Object.keys(this.credentials).length != 0) { - if (this.authType === "basic") { - // Workaround for https://github.com/sindresorhus/got/issues/1169 (fixed in got v12) - // var cred = "" - if (this.credentials.user || this.credentials.password) { - // cred = `${this.credentials.user}:${this.credentials.password}`; - if (this.credentials.user === undefined) { this.credentials.user = ""} - if (this.credentials.password === undefined) { this.credentials.password = ""} - opts.headers.Authorization = "Basic " + Buffer.from(`${this.credentials.user}:${this.credentials.password}`).toString("base64"); - } - // build own basic auth header - // opts.headers.Authorization = "Basic " + Buffer.from(cred).toString("base64"); - } else if (this.authType === "digest") { - let digestCreds = this.credentials; - let sentCreds = false; - opts.hooks.afterResponse = [(response, retry) => { - if (response.statusCode === 401) { - if (sentCreds) { - return response - } - const requestUrl = new URL(response.request.requestUrl); - const options = response.request.options; - const normalisedHeaders = {}; - Object.keys(response.headers).forEach(k => { - normalisedHeaders[k.toLowerCase()] = response.headers[k] - }) - if (normalisedHeaders['www-authenticate']) { - let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, options.method, requestUrl.pathname, normalisedHeaders['www-authenticate']) - options.headers.Authorization = authHeader; - } - sentCreds = true; - return retry(options); - } - return response - }]; - } else if (this.authType === "bearer") { - opts.headers.Authorization = `Bearer ${this.credentials.password||""}` - } - } - var payload = null; - - - if (method !== 'GET' && method !== 'HEAD' && typeof msg.payload !== "undefined") { - if (opts.headers['content-type'] == 'multipart/form-data' && typeof msg.payload === "object") { - let formData = new FormData(); - for (var opt in msg.payload) { - if (msg.payload.hasOwnProperty(opt)) { - var val = msg.payload[opt]; - if (val !== undefined && val !== null) { - if (typeof val === 'string' || Buffer.isBuffer(val)) { - formData.append(opt, val); - } else if (typeof val === 'object' && val.hasOwnProperty('value')) { - formData.append(opt,val.value,val.options || {}); - } else { - formData.append(opt,JSON.stringify(val)); - } - } - } - } - // GOT will only set the content-type header with the correct boundary - // if the header isn't set. So we delete it here, for GOT to reset it. - delete opts.headers['content-type']; - opts.body = formData; - } else { - if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) { - payload = msg.payload; - } else if (typeof msg.payload == "number") { - payload = msg.payload+""; - } else { - if (opts.headers['content-type'] == 'application/x-www-form-urlencoded') { - payload = querystring.stringify(msg.payload); - } else { - payload = JSON.stringify(msg.payload); - if (opts.headers['content-type'] == null) { - opts.headers[ctSet] = "application/json"; - } - } - } - if (opts.headers['content-length'] == null) { - if (Buffer.isBuffer(payload)) { - opts.headers[clSet] = payload.length; - } else { - opts.headers[clSet] = Buffer.byteLength(payload); - } - } - opts.body = payload; - } - } - - - if (method == 'GET' && typeof msg.payload !== "undefined" && paytoqs) { - if (typeof msg.payload === "object") { - try { - if (url.indexOf("?") !== -1) { - url += (url.endsWith("?")?"":"&") + querystring.stringify(msg.payload); - } else { - url += "?" + querystring.stringify(msg.payload); - } - } catch(err) { - - node.error(RED._("httpin.errors.invalid-payload"),msg); - nodeDone(); - return; - } - } else { - - node.error(RED._("httpin.errors.invalid-payload"),msg); - nodeDone(); - return; - } - } else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) { - opts.allowGetBody = true; - if (typeof msg.payload === "object") { - opts.body = JSON.stringify(msg.payload); - } else if (typeof msg.payload == "number") { - opts.body = msg.payload+""; - } else if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) { - opts.body = msg.payload; - } - } - - // revert to user supplied Capitalisation if needed. - if (opts.headers.hasOwnProperty('content-type') && (ctSet !== 'content-type')) { - opts.headers[ctSet] = opts.headers['content-type']; - delete opts.headers['content-type']; - } - if (opts.headers.hasOwnProperty('content-length') && (clSet !== 'content-length')) { - opts.headers[clSet] = opts.headers['content-length']; - delete opts.headers['content-length']; - } - - var noproxy; - if (noprox) { - for (var i = 0; i < noprox.length; i += 1) { - if (url.indexOf(noprox[i]) !== -1) { noproxy=true; } - } - } - if (prox && !noproxy) { - var match = prox.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i); - if (match) { - let proxyAgent; - let proxyURL = new URL(prox); - //set username/password to null to stop empty creds header - let proxyOptions = { - proxy: { - protocol: proxyURL.protocol, - hostname: proxyURL.hostname, - port: proxyURL.port, - username: null, - password: null - }, - maxFreeSockets: 256, - maxSockets: 256, - keepAlive: true - } - if (proxyConfig && proxyConfig.credentials) { - let proxyUsername = proxyConfig.credentials.username || ''; - let proxyPassword = proxyConfig.credentials.password || ''; - if (proxyUsername || proxyPassword) { - proxyOptions.proxy.username = proxyUsername; - proxyOptions.proxy.password = proxyPassword; - } - } else if (proxyURL.username || proxyURL.password){ - proxyOptions.proxy.username = proxyURL.username; - proxyOptions.proxy.password = proxyURL.password; - } - //need both incase of http -> https redirect - opts.agent = { - http: new HttpProxyAgent(proxyOptions), - https: new HttpsProxyAgent(proxyOptions) - }; - - } else { - node.warn("Bad proxy url: "+ prox); - } - } - if (tlsNode) { - opts.https = {}; - tlsNode.addTLSOptions(opts.https); - if (opts.https.ca) { - opts.https.certificateAuthority = opts.https.ca; - delete opts.https.ca; - } - if (opts.https.cert) { - opts.https.certificate = opts.https.cert; - delete opts.https.cert; - } - } else { - if (msg.hasOwnProperty('rejectUnauthorized')) { - opts.https = { rejectUnauthorized: msg.rejectUnauthorized }; - } - } - - // Now we have established all of our own headers, take a snapshot - // of their case so we can restore it prior to the request being sent. - if (opts.headers) { - Object.keys(opts.headers).forEach(h => { - originalHeaderMap[h.toLowerCase()] = h - }) - } - got(url,opts).then(res => { - msg.statusCode = res.statusCode; - msg.headers = res.headers; - msg.responseUrl = res.url; - msg.payload = res.body; - msg.redirectList = redirectList; - msg.retry = 0; - - if (msg.headers.hasOwnProperty('set-cookie')) { - msg.responseCookies = extractCookies(msg.headers['set-cookie']); - } - msg.headers['x-node-red-request-node'] = hashSum(msg.headers); - // msg.url = url; // revert when warning above finally removed - if (node.metric()) { - // Calculate request time - var diff = process.hrtime(preRequestTimestamp); - var ms = diff[0] * 1e3 + diff[1] * 1e-6; - var metricRequestDurationMillis = ms.toFixed(3); - node.metric("duration.millis", msg, metricRequestDurationMillis); - if (res.client && res.client.bytesRead) { - node.metric("size.bytes", msg, res.client.bytesRead); - } - if (timingLog) { - emitTimingMetricLog(res.timings, msg); - } - } - - // Convert the payload to the required return type - if (node.ret !== "bin") { - msg.payload = msg.payload.toString('utf8'); // txt - - if (node.ret === "obj") { - try { msg.payload = JSON.parse(msg.payload); } // obj - catch(e) { node.warn(RED._("httpin.errors.json-error")); } - } - } - node.status({}); - nodeSend(msg); - nodeDone(); - }).catch(err => { - // Pre 2.1, any errors would be sent to both Catch node and sent on as normal. - // This is not ideal but is the legacy behaviour of the node. - // 2.1 adds the 'senderr' option, if set to true, will *only* send errors - // to Catch nodes. If false, it still does both behaviours. - // TODO: 3.0 - make it one or the other. - - if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') { - node.error(RED._("common.notification.errors.no-response"), msg); - node.status({fill:"red", shape:"ring", text:"common.notification.errors.no-response"}); - } else { - node.error(err,msg); - node.status({fill:"red", shape:"ring", text:err.code}); - } - msg.payload = err.toString() + " : " + url; - msg.statusCode = err.code || (err.response?err.response.statusCode:undefined); - if (node.metric() && timingLog) { - emitTimingMetricLog(err.timings, msg); - } - if (!sendErrorsToCatch) { - nodeSend(msg); - } - nodeDone(); - }); - }); - - this.on("close",function() { - node.status({}); - }); - - function emitTimingMetricLog(timings, msg) { - const props = [ - "start", - "socket", - "lookup", - "connect", - "secureConnect", - "upload", - "response", - "end", - "error", - "abort" - ]; - if (timings) { - props.forEach(p => { - if (timings[p]) { - node.metric(`timings.${p}`, msg, timings[p]); - } - }); - } - } - - function extractCookies(setCookie) { - var cookies = {}; - setCookie.forEach(function(c) { - var parsedCookie = cookie.parse(c); - var eq_idx = c.indexOf('='); - var key = c.substr(0, eq_idx).trim() - parsedCookie.value = parsedCookie[key]; - delete parsedCookie[key]; - cookies[key] = parsedCookie; - }); - return cookies; - } - } - - RED.nodes.registerType("http request",HTTPRequest,{ - credentials: { - user: {type:"text"}, - password: {type: "password"} - } - }); - - const md5 = (value) => { return crypto.createHash('md5').update(value).digest('hex') } - - function ha1Compute(algorithm, user, realm, pass, nonce, cnonce) { - /** - * RFC 2617: handle both MD5 and MD5-sess algorithms. - * - * If the algorithm directive's value is "MD5" or unspecified, then HA1 is - * HA1=MD5(username:realm:password) - * If the algorithm directive's value is "MD5-sess", then HA1 is - * HA1=MD5(MD5(username:realm:password):nonce:cnonce) - */ - var ha1 = md5(user + ':' + realm + ':' + pass) - if (algorithm && algorithm.toLowerCase() === 'md5-sess') { - return md5(ha1 + ':' + nonce + ':' + cnonce) - } else { - return ha1 - } - } - - - function buildDigestHeader(user, pass, method, path, authHeader) { - var challenge = {} - var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi - for (;;) { - var match = re.exec(authHeader) - if (!match) { - break - } - challenge[match[1]] = match[2] || match[3] - } - var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth' - var nc = qop && '00000001' - var cnonce = qop && uuid().replace(/-/g, '') - var ha1 = ha1Compute(challenge.algorithm, user, challenge.realm, pass, challenge.nonce, cnonce) - var ha2 = md5(method + ':' + path) - var digestResponse = qop - ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2) - : md5(ha1 + ':' + challenge.nonce + ':' + ha2) - var authValues = { - username: user, - realm: challenge.realm, - nonce: challenge.nonce, - uri: path, - qop: qop, - response: digestResponse, - nc: nc, - cnonce: cnonce, - algorithm: challenge.algorithm, - opaque: challenge.opaque - } - - authHeader = [] - for (var k in authValues) { - if (authValues[k]) { - if (k === 'qop' || k === 'nc' || k === 'algorithm') { - authHeader.push(k + '=' + authValues[k]) - } else { - authHeader.push(k + '="' + authValues[k] + '"') - } - } - } - authHeader = 'Digest ' + authHeader.join(', ') - return authHeader - } -} diff --git a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html b/packages/node_modules/@node-red/nodes/core/network/22-websocket.html deleted file mode 100644 index a7b54d72f..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/22-websocket.js b/packages/node_modules/@node-red/nodes/core/network/22-websocket.js deleted file mode 100644 index ed4c93b09..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/22-websocket.js +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - var ws = require("ws"); - var inspect = require("util").inspect; - var url = require("url"); - var HttpsProxyAgent = require('https-proxy-agent'); - - - var serverUpgradeAdded = false; - function handleServerUpgrade(request, socket, head) { - const pathname = url.parse(request.url).pathname; - if (listenerNodes.hasOwnProperty(pathname)) { - listenerNodes[pathname].server.handleUpgrade(request, socket, head, function done(ws) { - listenerNodes[pathname].server.emit('connection', ws, request); - }); - } else { - // Don't destroy the socket as other listeners may want to handle the - // event. - } - } - var listenerNodes = {}; - var activeListenerNodes = 0; - - - // A node red node that sets up a local websocket server - function WebSocketListenerNode(n) { - // Create a RED node - RED.nodes.createNode(this,n); - var node = this; - - // Store local copies of the node configuration (as defined in the .html) - node.path = n.path; - if (typeof n.subprotocol === "string") { - // Split the string on comma and trim each result - node.subprotocol = n.subprotocol.split(",").map(v => v.trim()) - } else { - node.subprotocol = []; - } - node.wholemsg = (n.wholemsg === "true"); - - node._inputNodes = []; // collection of nodes that want to receive events - node._clients = {}; - // match absolute url - node.isServer = !/^ws{1,2}:\/\//i.test(node.path); - node.closing = false; - node.tls = n.tls; - - if (n.hb) { - var heartbeat = parseInt(n.hb); - if (heartbeat > 0) { - node.heartbeat = heartbeat * 1000; - } - } - - function startconn() { // Connect to remote endpoint - node.tout = null; - var prox, noprox; - if (process.env.http_proxy) { prox = process.env.http_proxy; } - if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; } - if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); } - if (process.env.NO_PROXY) { noprox = process.env.NO_PROXY.split(","); } - - var noproxy = false; - if (noprox) { - for (var i in noprox) { - if (node.path.indexOf(noprox[i].trim()) !== -1) { noproxy=true; } - } - } - - var agent = undefined; - if (prox && !noproxy) { - agent = new HttpsProxyAgent(prox); - } - - var options = {}; - if (agent) { - options.agent = agent; - } - if (node.tls) { - var tlsNode = RED.nodes.getNode(node.tls); - if (tlsNode) { - tlsNode.addTLSOptions(options); - } - } - var socket = new ws(node.path,node.subprotocol,options); - socket.setMaxListeners(0); - node.server = socket; // keep for closing - handleConnection(socket); - } - - function handleConnection(/*socket*/socket) { - var id = RED.util.generateId(); - socket.nrId = id; - socket.nrPendingHeartbeat = false; - if (node.isServer) { - node._clients[id] = socket; - node.emit('opened',{count:Object.keys(node._clients).length,id:id}); - } - socket.on('open',function() { - if (!node.isServer) { - if (node.heartbeat) { - clearInterval(node.heartbeatInterval); - node.heartbeatInterval = setInterval(function() { - if (socket.nrPendingHeartbeat) { - // No pong received - socket.terminate(); - socket.nrErrorHandler(new Error("timeout")); - return; - } - socket.nrPendingHeartbeat = true; - try { - socket.ping(); - } catch(err) {} - },node.heartbeat); - } - node.emit('opened',{count:'',id:id}); - } - }); - socket.on('close',function() { - clearInterval(node.heartbeatInterval); - if (node.isServer) { - delete node._clients[id]; - node.emit('closed',{count:Object.keys(node._clients).length,id:id}); - } else { - node.emit('closed',{count:'',id:id}); - } - if (!node.closing && !node.isServer) { - clearTimeout(node.tout); - node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ? - } - }); - socket.on('message',function(data,flags) { - node.handleEvent(id,socket,'message',data,flags); - }); - socket.nrErrorHandler = function(err) { - clearInterval(node.heartbeatInterval); - node.emit('erro',{err:err,id:id}); - if (!node.closing && !node.isServer) { - clearTimeout(node.tout); - node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ? - } - } - socket.on('error',socket.nrErrorHandler); - socket.on('ping', function() { - socket.nrPendingHeartbeat = false; - }) - socket.on('pong', function() { - socket.nrPendingHeartbeat = false; - }) - } - - if (node.isServer) { - activeListenerNodes++; - if (!serverUpgradeAdded) { - RED.server.on('upgrade', handleServerUpgrade); - serverUpgradeAdded = true - } - - var path = RED.settings.httpNodeRoot || "/"; - path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path); - node.fullPath = path; - - if (listenerNodes.hasOwnProperty(path)) { - node.error(RED._("websocket.errors.duplicate-path",{path: node.path})); - return; - } - listenerNodes[node.fullPath] = node; - var serverOptions = { - noServer: true - } - if (RED.settings.webSocketNodeVerifyClient) { - serverOptions.verifyClient = RED.settings.webSocketNodeVerifyClient; - } - // Create a WebSocket Server - node.server = new ws.Server(serverOptions); - node.server.setMaxListeners(0); - node.server.on('connection', handleConnection); - // Not adding server-initiated heartbeats yet - // node.heartbeatInterval = setInterval(function() { - // node.server.clients.forEach(function(ws) { - // if (ws.nrPendingHeartbeat) { - // // No pong received - // ws.terminate(); - // ws.nrErrorHandler(new Error("timeout")); - // return; - // } - // ws.nrPendingHeartbeat = true; - // ws.ping(); - // }); - // }) - } - else { - node.closing = false; - startconn(); // start outbound connection - } - - node.on("close", function() { - if (node.heartbeatInterval) { - clearInterval(node.heartbeatInterval); - } - if (node.isServer) { - delete listenerNodes[node.fullPath]; - node.server.close(); - node._inputNodes = []; - activeListenerNodes--; - // if (activeListenerNodes === 0 && serverUpgradeAdded) { - // RED.server.removeListener('upgrade', handleServerUpgrade); - // serverUpgradeAdded = false; - // } - } - else { - node.closing = true; - node.server.close(); - if (node.tout) { - clearTimeout(node.tout); - node.tout = null; - } - } - }); - } - RED.nodes.registerType("websocket-listener",WebSocketListenerNode); - RED.nodes.registerType("websocket-client",WebSocketListenerNode); - - WebSocketListenerNode.prototype.registerInputNode = function(/*Node*/handler) { - this._inputNodes.push(handler); - } - - WebSocketListenerNode.prototype.removeInputNode = function(/*Node*/handler) { - this._inputNodes.forEach(function(node, i, inputNodes) { - if (node === handler) { - inputNodes.splice(i, 1); - } - }); - } - - WebSocketListenerNode.prototype.handleEvent = function(id,/*socket*/socket,/*String*/event,/*Object*/data,/*Object*/flags) { - var msg; - if (this.wholemsg) { - try { - msg = JSON.parse(data); - if (typeof msg !== "object" && !Array.isArray(msg) && (msg !== null)) { - msg = { payload:msg }; - } - } - catch(err) { - msg = { payload:data }; - } - } else { - msg = { - payload:data - }; - } - msg._session = {type:"websocket",id:id}; - for (var i = 0; i < this._inputNodes.length; i++) { - this._inputNodes[i].send(msg); - } - } - - WebSocketListenerNode.prototype.broadcast = function(data) { - if (this.isServer) { - for (let client in this._clients) { - if (this._clients.hasOwnProperty(client)) { - try { - this._clients[client].send(data); - } catch(err) { - this.warn(RED._("websocket.errors.send-error")+" "+client+" "+err.toString()) - } - } - } - } - else { - try { - this.server.send(data); - } catch(err) { - this.warn(RED._("websocket.errors.send-error")+" "+err.toString()) - } - } - } - - WebSocketListenerNode.prototype.reply = function(id,data) { - var session = this._clients[id]; - if (session) { - try { - session.send(data); - } - catch(e) { // swallow any errors - } - } - } - - function WebSocketInNode(n) { - RED.nodes.createNode(this,n); - this.server = (n.client)?n.client:n.server; - var node = this; - this.serverConfig = RED.nodes.getNode(this.server); - if (this.serverConfig) { - this.serverConfig.registerInputNode(this); - // TODO: nls - this.serverConfig.on('opened', function(event) { - node.status({ - fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count}), - event:"connect", - _session: {type:"websocket",id:event.id} - }); - }); - this.serverConfig.on('erro', function(event) { - node.status({ - fill:"red",shape:"ring",text:"common.status.error", - event:"error", - _session: {type:"websocket",id:event.id} - }); - }); - this.serverConfig.on('closed', function(event) { - var status; - if (event.count > 0) { - status = {fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count})}; - } else { - status = {fill:"red",shape:"ring",text:"common.status.disconnected"}; - } - status.event = "disconnect"; - status._session = {type:"websocket",id:event.id} - node.status(status); - }); - } else { - this.error(RED._("websocket.errors.missing-conf")); - } - this.on('close', function() { - if (node.serverConfig) { - node.serverConfig.removeInputNode(node); - } - node.status({}); - }); - } - RED.nodes.registerType("websocket in",WebSocketInNode); - - function WebSocketOutNode(n) { - RED.nodes.createNode(this,n); - var node = this; - this.server = (n.client)?n.client:n.server; - this.serverConfig = RED.nodes.getNode(this.server); - if (!this.serverConfig) { - return this.error(RED._("websocket.errors.missing-conf")); - } - else { - // TODO: nls - this.serverConfig.on('opened', function(event) { - node.status({ - fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count}), - event:"connect", - _session: {type:"websocket",id:event.id} - }); - }); - this.serverConfig.on('erro', function(event) { - node.status({ - fill:"red",shape:"ring",text:"common.status.error", - event:"error", - _session: {type:"websocket",id:event.id} - }) - }); - this.serverConfig.on('closed', function(event) { - var status; - if (event.count > 0) { - status = {fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:event.count})}; - } else { - status = {fill:"red",shape:"ring",text:"common.status.disconnected"}; - } - status.event = "disconnect"; - status._session = {type:"websocket",id:event.id} - node.status(status); - }); - } - this.on("input", function(msg, nodeSend, nodeDone) { - var payload; - if (this.serverConfig.wholemsg) { - var sess; - if (msg._session) { sess = JSON.stringify(msg._session); } - delete msg._session; - payload = JSON.stringify(msg); - if (sess) { msg._session = JSON.parse(sess); } - } - else if (msg.hasOwnProperty("payload")) { - if (!Buffer.isBuffer(msg.payload)) { // if it's not a buffer make sure it's a string. - payload = RED.util.ensureString(msg.payload); - } - else { - payload = msg.payload; - } - } - if (payload) { - if (msg._session && msg._session.type == "websocket") { - node.serverConfig.reply(msg._session.id,payload); - } else { - node.serverConfig.broadcast(payload,function(error) { - if (!!error) { - node.warn(RED._("websocket.errors.send-error")+inspect(error)); - } - }); - } - } - nodeDone(); - }); - this.on('close', function() { - node.status({}); - }); - } - RED.nodes.registerType("websocket out",WebSocketOutNode); -} diff --git a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html deleted file mode 100644 index 97c8eb4d7..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js deleted file mode 100644 index 24e6abf7e..000000000 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.js +++ /dev/null @@ -1,852 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - let reconnectTime = RED.settings.socketReconnectTime || 10000; - let socketTimeout = RED.settings.socketTimeout || null; - const msgQueueSize = RED.settings.tcpMsgQueueSize || 1000; - const Denque = require('denque'); - const net = require('net'); - const tls = require('tls'); - - let connectionPool = {}; - - function normalizeConnectArgs(listArgs) { - const args = net._normalizeArgs(listArgs); - const options = args[0]; - const cb = args[1]; - - // If args[0] was options, then normalize dealt with it. - // If args[0] is port, or args[0], args[1] is host, port, we need to - // find the options and merge them in, normalize's options has only - // the host/port/path args that it knows about, not the tls options. - // This means that options.host overrides a host arg. - if (listArgs[1] !== null && typeof listArgs[1] === 'object') { - ObjectAssign(options, listArgs[1]); - } else if (listArgs[2] !== null && typeof listArgs[2] === 'object') { - ObjectAssign(options, listArgs[2]); - } - - return cb ? [options, cb] : [options]; - } - - function getAllowUnauthorized() { - const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0'; - - if (allowUnauthorized) { - process.emitWarning( - 'Setting the NODE_TLS_REJECT_UNAUTHORIZED ' + - 'environment variable to \'0\' makes TLS connections ' + - 'and HTTPS requests insecure by disabling ' + - 'certificate verification.'); - } - return allowUnauthorized; - } - - /** - * Enqueue `item` in `queue` - * @param {Denque} queue - Queue - * @param {*} item - Item to enqueue - * @private - * @returns {Denque} `queue` - */ - const enqueue = (queue, item) => { - // drop msgs from front of queue if size is going to be exceeded - if (queue.length === msgQueueSize) { queue.shift(); } - queue.push(item); - return queue; - }; - - /** - * Shifts item off front of queue - * @param {Deque} queue - Queue - * @private - * @returns {*} Item previously at front of queue - */ - const dequeue = queue => queue.shift(); - - function TcpIn(n) { - RED.nodes.createNode(this,n); - this.host = n.host; - this.port = n.port * 1; - this.topic = n.topic; - this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/ - this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */ - this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t"); - this.base64 = n.base64; - this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server"); - this.closing = false; - this.connected = false; - var node = this; - var count = 0; - if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); } - - if (!node.server) { - var buffer = null; - var client; - var reconnectTimeout; - var end = false; - var setupTcpClient = function() { - node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); - node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); - var id = RED.util.generateId(); - var connOpts = {host: node.host}; - if (n.tls) { - var connOpts = tlsNode.addTLSOptions({host: node.host}); - client = tls.connect(node.port, connOpts, function() { - buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - node.connected = true; - node.log(RED._("status.connected", {host: node.host, port: node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); - }); - } - else { - client = net.connect(node.port, node.host, function() { - buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - node.connected = true; - node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}}); - }); - } - client.setKeepAlive(true, 120000); - connectionPool[id] = client; - - client.on('data', function (data) { - if (node.datatype != 'buffer') { - data = data.toString(node.datatype); - } - if (node.stream) { - var msg; - if ((node.datatype) === "utf8" && node.newline !== "") { - buffer = buffer+data; - var parts = buffer.split(node.newline); - for (var i = 0; i 0)) { - var msg = {topic:node.topic, payload:buffer}; - msg._session = {type:"tcp",id:id}; - if (buffer.length !== 0) { - end = true; // only ask for fast re-connect if we actually got something - node.send(msg); - } - buffer = null; - } - }); - client.on('close', function() { - delete connectionPool[id]; - node.connected = false; - node.status({fill:"red",shape:"ring",text:"common.status.disconnected",_session:{type:"tcp",id:id}}); - if (!node.closing) { - if (end) { // if we were asked to close then try to reconnect once very quick. - end = false; - reconnectTimeout = setTimeout(setupTcpClient, 20); - } - else { - node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port})); - reconnectTimeout = setTimeout(setupTcpClient, reconnectTime); - } - } else { - if (node.doneClose) { node.doneClose(); } - } - }); - client.on('error', function(err) { - node.log(err); - }); - } - setupTcpClient(); - - this.on('close', function(done) { - node.doneClose = done; - this.closing = true; - if (client) { client.destroy(); } - clearTimeout(reconnectTimeout); - if (!node.connected) { done(); } - }); - } - else { - let srv = net; - let connOpts; - if (n.tls) { - srv = tls; - connOpts = tlsNode.addTLSOptions({}); - } - var server = srv.createServer(connOpts, function (socket) { - socket.setKeepAlive(true,120000); - if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } - var id = RED.util.generateId(); - var fromi; - var fromp; - connectionPool[id] = socket; - count++; - node.status({ - text:RED._("tcpin.status.connections",{count:count}), - event:"connect", - ip:socket.remoteAddress, - port:socket.remotePort, - _session: {type:"tcp",id:id} - }); - - var buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - socket.on('data', function (data) { - if (node.datatype != 'buffer') { - data = data.toString(node.datatype); - } - if (node.stream) { - var msg; - if ((typeof data) === "string" && node.newline !== "") { - buffer = buffer+data; - var parts = buffer.split(node.newline); - for (var i = 0; i 0) { - var msg = {topic:node.topic, payload:buffer, ip:fromi, port:fromp}; - msg._session = {type:"tcp",id:id}; - node.send(msg); - } - buffer = null; - } - }); - socket.on('timeout', function() { - node.log(RED._("tcpin.errors.timeout",{port:node.port})); - socket.end(); - }); - socket.on('close', function() { - delete connectionPool[id]; - count--; - node.status({ - text:RED._("tcpin.status.connections",{count:count}), - event:"disconnect", - ip:socket.remoteAddress, - port:socket.remotePort, - _session: {type:"tcp",id:id} - - }); - }); - socket.on('error',function(err) { - node.log(err); - }); - }); - - server.on('error', function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } - }); - - server.listen(node.port, function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } else { - node.log(RED._("tcpin.status.listening-port",{port:node.port})); - node.on('close', function() { - for (var c in connectionPool) { - if (connectionPool.hasOwnProperty(c)) { - connectionPool[c].end(); - connectionPool[c].unref(); - } - } - node.closing = true; - server.close(); - node.log(RED._("tcpin.status.stopped-listening",{port:node.port})); - }); - } - }); - } - } - RED.nodes.registerType("tcp in",TcpIn); - - - function TcpOut(n) { - RED.nodes.createNode(this,n); - this.host = n.host; - this.port = n.port * 1; - this.base64 = n.base64; - this.doend = n.end || false; - this.beserver = n.beserver; - this.name = n.name; - this.closing = false; - this.connected = false; - var node = this; - if (n.tls) { var tlsNode = RED.nodes.getNode(n.tls); } - - if (!node.beserver || node.beserver == "client") { - var reconnectTimeout; - var client = null; - var end = false; - - var setupTcpClient = function() { - node.log(RED._("tcpin.status.connecting",{host:node.host,port:node.port})); - node.status({fill:"grey",shape:"dot",text:"common.status.connecting"}); - if (n.tls) { - // connOpts = tlsNode.addTLSOptions(connOpts); - // client = tls.connect(connOpts, function() { - var connOpts = tlsNode.addTLSOptions({host: node.host}); - client = tls.connect(node.port, connOpts, function() { - // buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : ""; - node.connected = true; - node.log(RED._("status.connected", {host: node.host, port: node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - } - else { - client = net.connect(node.port, node.host, function() { - node.connected = true; - node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port})); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - } - client.setKeepAlive(true,120000); - client.on('error', function (err) { - node.log(RED._("tcpin.errors.error",{error:err.toString()})); - }); - client.on('end', function (err) { - node.status({}); - node.connected = false; - }); - client.on('close', function() { - node.status({fill:"red",shape:"ring",text:"common.status.disconnected"}); - node.connected = false; - client.destroy(); - if (!node.closing) { - if (end) { - end = false; - reconnectTimeout = setTimeout(setupTcpClient,20); - } - else { - node.log(RED._("tcpin.errors.connection-lost",{host:node.host,port:node.port})); - reconnectTimeout = setTimeout(setupTcpClient,reconnectTime); - } - } else { - if (node.doneClose) { node.doneClose(); } - } - }); - } - setupTcpClient(); - - node.on("input", function(msg, nodeSend, nodeDone) { - if (node.connected && msg.payload != null) { - if (Buffer.isBuffer(msg.payload)) { - client.write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - client.write(Buffer.from(msg.payload,'base64')); - } else { - client.write(Buffer.from(""+msg.payload)); - } - if (node.doend === true) { - end = true; - if (client) { node.status({}); client.destroy(); } - } - } - nodeDone(); - }); - - node.on("close", function(done) { - node.doneClose = done; - this.closing = true; - if (client) { client.destroy(); } - clearTimeout(reconnectTimeout); - if (!node.connected) { done(); } - }); - - } - else if (node.beserver == "reply") { - node.on("input",function(msg, nodeSend, nodeDone) { - if (msg._session && msg._session.type == "tcp") { - var client = connectionPool[msg._session.id]; - if (client) { - if (Buffer.isBuffer(msg.payload)) { - client.write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - client.write(Buffer.from(msg.payload,'base64')); - } else { - client.write(Buffer.from(""+msg.payload)); - } - } - } - else { - for (var i in connectionPool) { - if (Buffer.isBuffer(msg.payload)) { - connectionPool[i].write(msg.payload); - } else if (typeof msg.payload === "string" && node.base64) { - connectionPool[i].write(Buffer.from(msg.payload,'base64')); - } else { - connectionPool[i].write(Buffer.from(""+msg.payload)); - } - } - } - nodeDone(); - }); - } - else { - var connectedSockets = []; - node.status({text:RED._("tcpin.status.connections",{count:0})}); - let srv = net; - let connOpts; - if (n.tls) { - srv = tls; - connOpts = tlsNode.addTLSOptions({}); - } - var server = srv.createServer(connOpts, function (socket) { - socket.setKeepAlive(true,120000); - if (socketTimeout !== null) { socket.setTimeout(socketTimeout); } - node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort})); - socket.on('timeout', function() { - node.log(RED._("tcpin.errors.timeout",{port:node.port})); - socket.end(); - }); - socket.on('data', function(d) { - // console.log("DATA",d) - }); - socket.on('close',function() { - node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort})); - connectedSockets.splice(connectedSockets.indexOf(socket),1); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - socket.on('error',function() { - node.log(RED._("tcpin.errors.socket-error",{host:socket.remoteAddress, port:socket.remotePort})); - connectedSockets.splice(connectedSockets.indexOf(socket),1); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - connectedSockets.push(socket); - node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})}); - }); - - node.on("input", function(msg, nodeSend, nodeDone) { - if (msg.payload != null) { - var buffer; - if (Buffer.isBuffer(msg.payload)) { - buffer = msg.payload; - } else if (typeof msg.payload === "string" && node.base64) { - buffer = Buffer.from(msg.payload,'base64'); - } else { - buffer = Buffer.from(""+msg.payload); - } - for (var i = 0; i < connectedSockets.length; i += 1) { - if (node.doend === true) { connectedSockets[i].end(buffer); } - else { connectedSockets[i].write(buffer); } - } - } - nodeDone(); - }); - - server.on('error', function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } - }); - - server.listen(node.port, function(err) { - if (err) { - node.error(RED._("tcpin.errors.cannot-listen",{port:node.port,error:err.toString()})); - } else { - node.log(RED._("tcpin.status.listening-port",{port:node.port})); - node.on('close', function() { - for (var c in connectedSockets) { - if (connectedSockets.hasOwnProperty(c)) { - connectedSockets[c].end(); - connectedSockets[c].unref(); - } - } - server.close(); - node.log(RED._("tcpin.status.stopped-listening",{port:node.port})); - }); - } - }); - } - } - RED.nodes.registerType("tcp out",TcpOut); - - - function TcpGet(n) { - RED.nodes.createNode(this,n); - this.server = n.server; - this.port = Number(n.port); - this.out = n.out; - this.ret = n.ret || "buffer"; - this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t"); - this.splitc = n.splitc; - if (n.tls) { - var tlsNode = RED.nodes.getNode(n.tls); - } - - if (this.out === "immed") { this.splitc = -1; this.out = "time"; } - if (this.out !== "char") { this.splitc = Number(this.splitc); } - else { - if (this.splitc[0] == '\\') { - this.splitc = parseInt(this.splitc.replace("\\n",0x0A).replace("\\r",0x0D).replace("\\t",0x09).replace("\\e",0x1B).replace("\\f",0x0C).replace("\\0",0x00)); - } // jshint ignore:line - if (typeof this.splitc == "string") { - if (this.splitc.substr(0,2) == "0x") { - this.splitc = parseInt(this.splitc); - } - else { - this.splitc = this.splitc.charCodeAt(0); - } - } // jshint ignore:line - } - - var node = this; - - var clients = {}; - - this.on("input", function(msg, nodeSend, nodeDone) { - var i = 0; - if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) { - msg.payload = msg.payload.toString(); - } - - var host = node.server || msg.host; - var port = node.port || msg.port; - - // Store client information independently - // the clients object will have: - // clients[id].client, clients[id].msg, clients[id].timeout - var connection_id = host + ":" + port; - if (connection_id !== node.last_id) { - node.status({}); - node.last_id = connection_id; - } - clients[connection_id] = clients[connection_id] || { - msgQueue: new Denque(), - connected: false, - connecting: false - }; - enqueue(clients[connection_id].msgQueue, {msg:msg, nodeSend:nodeSend, nodeDone:nodeDone}); - clients[connection_id].lastMsg = msg; - - if (!clients[connection_id].connecting && !clients[connection_id].connected) { - var buf; - if (this.out == "count") { - if (this.splitc === 0) { buf = Buffer.alloc(1); } - else { buf = Buffer.alloc(this.splitc); } - } - else { buf = Buffer.alloc(65536); } // set it to 64k... hopefully big enough for most TCP packets.... but only hopefully - - var connOpts = {host:host, port:port}; - if (n.tls) { - connOpts = tlsNode.addTLSOptions(connOpts); - const allowUnauthorized = getAllowUnauthorized(); - - let options = { - rejectUnauthorized: !allowUnauthorized, - ciphers: tls.DEFAULT_CIPHERS, - checkServerIdentity: tls.checkServerIdentity, - minDHSize: 1024, - ...connOpts - }; - - if (!options.keepAlive) { options.singleUse = true; } - - const context = options.secureContext || tls.createSecureContext(options); - - clients[connection_id].client = new tls.TLSSocket(options.socket, { - allowHalfOpen: options.allowHalfOpen, - pipe: !!options.path, - secureContext: context, - isServer: false, - requestCert: false, // true, - rejectUnauthorized: false, // options.rejectUnauthorized !== false, - session: options.session, - ALPNProtocols: options.ALPNProtocols, - requestOCSP: options.requestOCSP, - enableTrace: options.enableTrace, - pskCallback: options.pskCallback, - highWaterMark: options.highWaterMark, - onread: options.onread, - signal: options.signal, - }); - } - else { - clients[connection_id].client = net.Socket(); - } - if (socketTimeout !== null) { clients[connection_id].client.setTimeout(socketTimeout);} - - if (host && port) { - clients[connection_id].connecting = true; - clients[connection_id].client.connect(connOpts, function() { - //node.log(RED._("tcpin.errors.client-connected")); - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].connected = true; - clients[connection_id].connecting = false; - let event; - while (event = dequeue(clients[connection_id].msgQueue)) { - clients[connection_id].client.write(event.msg.payload); - event.nodeDone(); - } - if (node.out === "time" && node.splitc < 0) { - clients[connection_id].connected = clients[connection_id].connecting = false; - clients[connection_id].client.end(); - delete clients[connection_id]; - node.status({}); - } - } - }); - } - else { - node.warn(RED._("tcpin.errors.no-host")); - } - var chunk = ""; - clients[connection_id].client.on('data', function(data) { - if (node.out === "sit") { // if we are staying connected just send the buffer - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = RED.util.cloneMessage(data); - if (node.ret === "string") { - try { - if (node.newline && node.newline !== "" ) { - chunk += msg.payload.toString(); - let parts = chunk.split(node.newline); - for (var p=0; p= node.splitc) { - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i); - buf.copy(msg.payload,0,0,i); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - i = 0; - } - } - } - // look for a char - else { - buf[i] = data[j]; - i += 1; - if (data[j] == node.splitc) { - if (clients[connection_id]) { - const msg = clients[connection_id].lastMsg || {}; - msg.payload = Buffer.alloc(i); - buf.copy(msg.payload,0,0,i); - if (node.ret === "string") { - try { msg.payload = msg.payload.toString(); } - catch(e) { node.error("Failed to create string", msg); } - } - nodeSend(msg); - if (clients[connection_id].client) { - node.status({}); - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - i = 0; - } - } - } - } - } - }); - - clients[connection_id].client.on('end', function() { - //console.log("END"); - node.status({fill:"grey",shape:"ring",text:"common.status.disconnected"}); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].connected = clients[connection_id].connecting = false; - clients[connection_id].client = null; - } - }); - - clients[connection_id].client.on('close', function() { - //console.log("CLOSE"); - if (clients[connection_id]) { - clients[connection_id].connected = clients[connection_id].connecting = false; - } - - var anyConnected = false; - - for (var client in clients) { - if (clients[client].connected) { - anyConnected = true; - break; - } - } - if (node.doneClose && !anyConnected) { - clients = {}; - node.doneClose(); - } - }); - - clients[connection_id].client.on('error', function() { - //console.log("ERROR"); - node.status({fill:"red",shape:"ring",text:"common.status.error"}); - node.error(RED._("tcpin.errors.connect-fail") + " " + connection_id, msg); - if (clients[connection_id] && clients[connection_id].client) { - clients[connection_id].client.destroy(); - delete clients[connection_id]; - } - }); - - clients[connection_id].client.on('timeout',function() { - //console.log("TIMEOUT"); - if (clients[connection_id]) { - clients[connection_id].connected = clients[connection_id].connecting = false; - node.status({fill:"grey",shape:"dot",text:"tcpin.errors.connect-timeout"}); - //node.warn(RED._("tcpin.errors.connect-timeout")); - if (clients[connection_id].client) { - clients[connection_id].connecting = true; - - var connOpts = {host:host, port:port}; - if (n.tls) { - connOpts = tlsNode.addTLSOptions(connOpts); - } - - clients[connection_id].client.connect(connOpts, function() { - clients[connection_id].connected = true; - clients[connection_id].connecting = false; - node.status({fill:"green",shape:"dot",text:"common.status.connected"}); - }); - } - } - }); - } - else if (!clients[connection_id].connecting && clients[connection_id].connected) { - if (clients[connection_id] && clients[connection_id].client) { - let event = dequeue(clients[connection_id].msgQueue) - clients[connection_id].client.write(event.msg.payload); - event.nodeDone(); - } - } - }); - - this.on("close", function(done) { - node.doneClose = done; - for (var cl in clients) { - if (clients[cl].hasOwnProperty("client")) { - clients[cl].client.destroy(); - } - } - node.status({}); - - // this is probably not necessary and may be removed - var anyConnected = false; - for (var c in clients) { - if (clients[c].connected) { - anyConnected = true; - break; - } - } - if (!anyConnected) { clients = {}; } - done(); - }); - - } - RED.nodes.registerType("tcp request",TcpGet); -} diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js b/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js deleted file mode 100644 index e16c4ec23..000000000 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-JSON.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -module.exports = function(RED) { - "use strict"; - const Ajv = require('ajv'); - const ajv = new Ajv({allErrors: true}); - ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); - - function JSONNode(n) { - RED.nodes.createNode(this,n); - this.indent = n.pretty ? 4 : 0; - this.action = n.action||""; - this.property = n.property||"payload"; - this.schema = null; - this.compiledSchema = null; - - var node = this; - - this.on("input", function(msg,send,done) { - var validate = false; - if (msg.schema) { - // If input schema is different, re-compile it - if (JSON.stringify(this.schema) != JSON.stringify(msg.schema)) { - try { - this.compiledSchema = ajv.compile(msg.schema); - this.schema = msg.schema; - } catch(e) { - this.schema = null; - this.compiledSchema = null; - done(RED._("json.errors.schema-error-compile")); - return; - } - } - validate = true; - } - var value = RED.util.getMessageProperty(msg,node.property); - if (value !== undefined) { - if (typeof value === "string" || Buffer.isBuffer(value)) { - // if (Buffer.isBuffer(value) && node.action !== "obj") { - // node.warn(RED._("json.errors.dropped")); done(); - // } - // else - if (node.action === "" || node.action === "obj") { - try { - RED.util.setMessageProperty(msg,node.property,JSON.parse(value)); - if (validate) { - if (this.compiledSchema(msg[node.property])) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - catch(e) { done(e.message); } - } else { - // If node.action is str and value is str - if (validate) { - if (this.compiledSchema(JSON.parse(msg[node.property]))) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - } - else if ((typeof value === "object") || (typeof value === "boolean") || (typeof value === "number")) { - if (node.action === "" || node.action === "str") { - if (!Buffer.isBuffer(value)) { - try { - if (validate) { - if (this.compiledSchema(value)) { - RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); - send(msg); - done(); - } - } - catch(e) { done(RED._("json.errors.dropped-error")); } - } - else { node.warn(RED._("json.errors.dropped-object")); done(); } - } else { - // If node.action is obj and value is object - if (validate) { - if (this.compiledSchema(value)) { - delete msg.schema; - send(msg); - done(); - } else { - msg.schemaError = this.compiledSchema.errors; - done(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`); - } - } else { - send(msg); - done(); - } - } - } - else { node.warn(RED._("json.errors.dropped")); done(); } - } - else { send(msg); done(); } // If no property - just pass it on. - }); - } - RED.nodes.registerType("json",JSONNode); -} diff --git a/packages/node_modules/@node-red/nodes/icons/guide.svg b/packages/node_modules/@node-red/nodes/icons/guide.svg new file mode 100644 index 000000000..b46237cfb --- /dev/null +++ b/packages/node_modules/@node-red/nodes/icons/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file From 36166b3bb22c9c0e6c8f38e5177e7a7e3de6ba60 Mon Sep 17 00:00:00 2001 From: Gavin Scallon Date: Thu, 24 Feb 2022 08:15:12 -0700 Subject: [PATCH 43/53] Added prereq information for installing npm and docker/podman. Added some supplemental info for less experienced Unicorns. --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f090049d..da2557651 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,26 @@ Sparkle's Guide is inspired by [roadmap.sh](https://roadmap.sh) and is a derivative open source work of [node-red](https://github.com/node-red/node-red). This project aims to be a guide to sparkles and unicorns in the [blessing](https://unicornyard.com/what-is-a-group-of-unicorns-called/) on their magical journey to better themselves and each other. ## Quick Start -Local Environment with NPM + +Begin by cloning the Sparkles-Guide repository to the machine using SSH (`git clone git@github.com:defenseunicorns/Sparkles-Guide.git`) or HTTPS (`git clone https://github.com/defenseunicorns/Sparkles-Guide.git`). + +After the project has been cloned, run `cd ./Sparkles-Guide` in the terminal in order to change the current working directory to the top level of the project to run the Quick Start commands as shown. + +There are two different methods that can be used in order to get the Sparkle's Guide up-and-running quickly once the project has been cloned: + +### Local Environment with NPM + +Prerequisite: Ensure that node and npm are installed on the machine. [This article provides information on installing both on Mac using Homebrew](https://medium.com/@hayasnc/how-to-install-nodejs-and-npm-on-mac-using-homebrew-b33780287d8f). + 1. `npm install` 2. `npm run build` 3. `npm start -- data/flows.json` 4. Open -Docker/Podman +### Docker/Podman + +Prerequisite: Ensure that docker or podman are installed on the machine. [This article provides information on installing Docker on Mac using Docker Desktop/Homebrew](https://www.cprime.com/resources/blog/docker-for-mac-with-homebrew-a-step-by-step-tutorial/). The Homebrew formula for [installing podman on a Mac can be found here](https://formulae.brew.sh/formula/podman). + 1. `./container-build.sh` 2. `./container-run.sh` 3. Open From cce9173e107a3d027c3f3da5aa5c9765221550b1 Mon Sep 17 00:00:00 2001 From: Jordan McClintock Date: Thu, 24 Feb 2022 12:25:10 -0600 Subject: [PATCH 44/53] Updated flows to use new node types --- data/flows.json | 137 +++++++++--------- .../@node-red/nodes/core/common/task.html | 2 +- 2 files changed, 70 insertions(+), 69 deletions(-) diff --git a/data/flows.json b/data/flows.json index e8985ee9d..3add6c9e2 100644 --- a/data/flows.json +++ b/data/flows.json @@ -62,7 +62,7 @@ }, { "id": "73521b98841c6355", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Kubernetes", "info": "# Description\n\n\n## Resources\n- https://kubernetes.io/docs/home/ ***\n- https://www.youtube.com/watch?v=VnvRFRk_51k \n- https://www.youtube.com/watch?v=X48VuDVv0do (long video, covers most topics in this flow)", @@ -79,7 +79,7 @@ }, { "id": "90665185044d3fc2", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Kubernetes Objects", "info": "", @@ -94,7 +94,7 @@ }, { "id": "ccf8bad6e9ebfb6a", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Kubernetes Networking", "info": "", @@ -106,7 +106,7 @@ }, { "id": "4146a7d5608cc03b", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Kubernetes Storage", "info": "", @@ -118,7 +118,7 @@ }, { "id": "418551f904d62b20", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Kubernetes App Manifests", "info": "", @@ -130,7 +130,7 @@ }, { "id": "0d309e7b063a4ce2", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Configurations", "info": "", @@ -145,7 +145,7 @@ }, { "id": "d810b83f64f0bc5e", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Deployments", "info": "", @@ -159,7 +159,7 @@ }, { "id": "752d2d1fc700d27e", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Pods", "info": "", @@ -171,7 +171,7 @@ }, { "id": "c53965f3ddc770ad", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Configmaps", "info": "", @@ -183,7 +183,7 @@ }, { "id": "e902145181ebd724", - "type": "comment", + "type": "resource", "z": "0ac7681ed1c2bc4b", "name": "Secrets", "info": "", @@ -195,7 +195,7 @@ }, { "id": "5dfd2ba38ec5178d", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Containerization", "info": "", @@ -209,7 +209,7 @@ }, { "id": "8c04bec4a15f6702", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Helm", "info": "", @@ -223,7 +223,7 @@ }, { "id": "fc01372231b580db", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Custom Resource Definitions", "info": "", @@ -237,7 +237,7 @@ }, { "id": "09d8e90d5b4ade12", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Kustomization", "info": "", @@ -251,7 +251,7 @@ }, { "id": "71a96ec4058930db", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "GitOps", "info": "", @@ -265,7 +265,7 @@ }, { "id": "98df2d11adb09046", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "git", "info": "# Description\n\n[Git](https://git-scm.com/) is a free and open source distributed version control system.\n\n## Resources\n- https://git-scm.com/docs\n\n\n## Examples\n- github.com\n- gitlab.com\n- https://gitea.com/", @@ -279,7 +279,7 @@ }, { "id": "45465f711df6da7e", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Flux", "info": "", @@ -293,7 +293,7 @@ }, { "id": "e328f5849309e731", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Big Bang", "info": "", @@ -322,7 +322,7 @@ }, { "id": "81348cdecdf51fd9", - "type": "comment", + "type": "resource", "z": "eb7909bcf5fcaf8a", "name": "Operators", "info": "https://www.youtube.com/watch?v=ha3LjlD6g7g", @@ -336,27 +336,28 @@ }, { "id": "40bfe328130d9c41", - "type": "comment", + "type": "guide", "z": "c86af370eff94afa", "name": "Deploy K3d", "info": "Throughout this guide you will be using K3d to deploy and manage kubernetes instances.\n\n## Choose your platform\nThe preferred free VM software is [VirtualBox](https://www.virtualbox.org/); however, it is not compatible with M1 chips! So, if you are using an M1 Mac, we highly recommend you go with the [Cloud](#Cloud) option below\n\n### Cloud\nRecommend using an EC2 instance with the following specs:\n- AMI: Ubuntu Server (64-bit, x86)\n- Instance Type: t3a.2xlarge\n- 100 GB EBS\n- Security group rules for allowing web traffic\n\n### Local VM\nUse [Vagrant](https://www.vagrantup.com/) to spin up a VM with at least the following specs:\n- 10GB RAM\n- 4 CPU cores\n- An IP that can be [accessed](https://www.vagrantup.com/docs/networking/private_network#static-ip) from the host machine. \n\n### Local\nAny Docker installation except a non-WSL2 Windows installation will work with K3d natively.\n\n## K3d setup\n\nSetup instructions for K3d can be found at the link below.\n\nhttps://k3d.io/v5.2.2/\n\n## Cluster creation\n\nOnce you have a host for deploying a k3d cluster - you can configure a cluster for future nodes in this training.\n\nOne possible configuration would be a 3 node cluster with 1 server and 2 agents (to get started).\n\nAnother consideration is exposing applications after the cluster is running and apps have been deployed w/ services and ingresses.\n\nDepending on RAM/CPU availability, you may want to run 2 or more K3d nodes\n\nA possible configuration might look like:\n\n`k3d cluster create -s 1 -a 2 -p \"8081:80@loadbalancer\" dev-cluster`\n\n-s 1 represents 1 server node\n-a 2 represents 2 agent nodes\n\n-p \"8081:80@loadbalancer\" represents mapping 8081 on this k3d host to ports 80 internally.", - "x": 510, - "y": 40, + "x": 360, + "y": 60, "wires": [ [ "691424f6cfd58372", - "1458b18889c13942" + "1458b18889c13942", + "6d2d525b92151db2" ] ] }, { "id": "691424f6cfd58372", - "type": "comment", + "type": "guide", "z": "c86af370eff94afa", "name": "Deploy a pod info application (with Kustomize)", "info": "# Kustomize\n\n\"Kustomize is a command-line configuration manager for Kubernetes objects. Integrated with kubectl since 1.14, it allows you to make declarative changes to your configurations without touching a template.\"\n\n## Recommended Reading\n\n`https://www.mirantis.com/blog/introduction-to-kustomize-part-1-creating-a-kubernetes-app-out-of-multiple-pieces/`\n\nThe above tutorial can be run on the k3d cluster you have created. This is a much more complex example than the podinfo example you will deploy below.\n\n## Podinfo\n\n\"Podinfo is a tiny web application made with Go that showcases best practices of running microservices in Kubernetes\"\n\n### Deployment\n\nNavigate to `https://github.com/stefanprodan/podinfo`\n\nClone the repository to your local machine\n\nFrom within the cloned directory, we can execute kustomize through the built in functionality in `kubectl`.\n\n`kubectl kustomize ./kustomize`\n\nThis will return the built manifest for the application to be deployed. You can then deploy the application to the cluster through `kubectl apply -k ./kustomize`\n\n## Success Criteria\n\n- 2 podinfo pods in the target namespace (default if not specified)\n- A podinfo service\n- A horizontal pod autoscaler\n\nWe can port-forward this applications service and visit it in browser to confirm functionality.\n\n`kubectl port-forward service/podinfo 9898:http`\n\nThis port fowards the podinfo service `http` port (as defined in the service) to the 9898 host port.", - "x": 630, - "y": 130, + "x": 420, + "y": 190, "wires": [ [ "2d66fece0a8f5fdf" @@ -365,12 +366,12 @@ }, { "id": "2d66fece0a8f5fdf", - "type": "comment", + "type": "task", "z": "c86af370eff94afa", "name": "Expose podinfo with an Ingress", "info": "# Ingress\n\nIn the previous exercise, we deployed the podinfo application and confirmed functionality by visiting it in-browser through port-forwarding with kubectl. We can instead use an ingress and the default traefik ingress-controller to handle this functionaity more natively. \n\nOfficial k3d docs: https://k3d.io/v5.0.0/usage/exposing_services/\n\n## Ingress deployment\nDue to the cluster configuration that we executed in the first node (See the -p loadbalancer parameter). we can configure an ingress to expose the application, as is one of a few standard practices for exposing internal applications to external traffic.\n\n### Ingress template\n\nIngress docs: https://kubernetes.io/docs/concepts/services-networking/ingress/\n\nGiven the template from the docs/tutorial we can write an ingress to support this traffic.\n\n## Success Criteria\n\nAfter the ingress is deployed (and given you configured your cluster as described in the first node), then you should be able to access the podinfo application without port-forwarding at `http://localhost:8081` \n\n## Cleanup\n\nThis concludes this deployment of podinfo. You'll want to cleanup the resources we have created. ", - "x": 630, - "y": 200, + "x": 420, + "y": 260, "wires": [ [ "52eb692e4c51108f" @@ -379,12 +380,12 @@ }, { "id": "52eb692e4c51108f", - "type": "comment", + "type": "guide", "z": "c86af370eff94afa", "name": "Create a podinfo helm chart", "info": "# Helm\n\"Helm is the package manager for Kubernetes\"\n\n## Recommended Reading\nhttps://helm.sh/\nhttps://helm.sh/docs/intro/\n\n## Podinfo Helm Chart from Scratch\n\nTODO - Insert content for:\n- deployment\n- service\n- ingress\n- HPA\n* All templated from scratch\n\n## Deploy the official podinfo chart from local files\n\nPreviously we cloned the podinfo repository to our/a local machine. Under the root of the project there is a `charts` directory with a `podinfo` directory that contains the podinfo chart content.\n\n## Basic deployment\n\nLet's create a testing namespace for our target\n`kubectl create ns testing`\n\nWith Helm installed and our k3d cluster still running/configured, we can install the chart in it's vanilla form (without enabling any additional content).\n\n(From the charts directory)\n`helm install podinfo-dev ./podinfo -n testing`\n\nThis will deploy the chart which results in the deployment and service creation in the target namespace.\n\n## Customizations\nWe can inject exposed customizations as outlined in the README/values.yaml for the purpose of configuring the end package being suited for our needs.\n\nWe can make an edit to the values.yaml and upgrade our application.\n\nhpa:\n enabled: true\n \n`helm upgrade podinfo-dev -n testing ./podinfo`\n\nThis should result in an HPA being deployed to our namespace for the application.\n", - "x": 630, - "y": 270, + "x": 420, + "y": 330, "wires": [ [ "5361df412a99db12" @@ -393,12 +394,12 @@ }, { "id": "5361df412a99db12", - "type": "comment", + "type": "guide", "z": "c86af370eff94afa", "name": "Deploy BigBang on a new K3d cluster", "info": "# Big Bang\n\nUsing our machine with k3d, we can instantiate a development/prototype deployment of Big Bang.\n\n## Objective\n\nFollow the qiuckstart here:\nhttps://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/master/docs/guides/deployment_scenarios/quickstart.md\n\n## Success Criteria\n\nAs noted in step 13 - the web UI's should be resolvable and accessible. All pods should be up and healthy (running). ", - "x": 630, - "y": 340, + "x": 420, + "y": 400, "wires": [ [ "67415f95eb8ab46c" @@ -407,12 +408,12 @@ }, { "id": "67415f95eb8ab46c", - "type": "comment", + "type": "task", "z": "c86af370eff94afa", "name": "Deploy podinfo helmchart in a bigbang cluster", "info": "# Big Bang Extensibility\n\nUp to now - we have configured and deployed k3d clusters, we've used `kustomize` and `Helm` to orchestrate deploying `podinfo` to our cluster.\n\nWe then used flux and it's controllers to deploy bigbang (which is using helm and kustomize under the hood).\n\n## Recommended Reading\n\n## Objective\nNow we will look at extending the big bang deployment to include deploying the podinfo helm chart and ensuring the deployment architecture aligns with the Big Bang \"Core\" technologies.\n\nThis is a precursory step to follow-on nodes in this flow. \n\n## Execution\n\nClone the podinfo repository if not done already.\n\nCreate a namespace with the label:\n`istio-injection: enabled`\n\nDeploy the helm chart for the podinfo applicaiton as we have done previously.\n\n## Success Criteria\nDeploy the podinfo helm chart into your cluster that already has big bang deployed.\n\nThe application should come online and be able to be reached via `kubectl port-forward`\n\nContinue to the next node for extending this with Istio", - "x": 630, - "y": 410, + "x": 420, + "y": 470, "wires": [ [ "9b8fdffdc9b5cc53" @@ -421,12 +422,12 @@ }, { "id": "9b8fdffdc9b5cc53", - "type": "comment", + "type": "task", "z": "c86af370eff94afa", "name": "Add Istio Virtual Service for the podinfo deployment", "info": "# Istio VirtualServices\n\n## Recommended Reading\n\n- https://istio.io/latest/docs/reference/config/networking/virtual-service/\n\n### Example\n- https://repo1.dso.mil/platform-one/big-bang/apps/core/kiali/-/blob/main/chart/values.yaml#:~:text=istio%3A,kiali.%7B%7B%20.Values.hostname%20%7D%7D\n- https://repo1.dso.mil/platform-one/big-bang/apps/core/kiali/-/blob/main/chart/templates/bigbang/virtualservice.yaml\n\n## Objective\nWith the podinfo helm chart deployed (in it's basic state), we will look to leverage some of th technologies that Big Bang has reconciled and configured.\n\nBig Bang exposes cluster applications to external traffic through an Istio Ingress Gateway.\n\nVirtual services are then used to link kubernetes services to traffic from the ingresss gateways.\n\n## Execution\n\n\n\n## Success Criteria\nWrite a virtual service resource definition file for the podinfo application that was deployed as part of the last node.\n\nIf you add the `podinfo.bigbang.dev` endpoint to your hosts file, then it should be resolveable from the browser.\n\n## Solution\n
    show\n

    \n\n```\napiVersion: networking.istio.io/v1beta1\nkind: VirtualService\nmetadata:\n name: podinfo\n namespace: podinfo\nspec:\n gateways:\n - istio-system/public\n hosts:\n - podinfo.bigbang.dev\n http:\n - route:\n - destination:\n host: podinfo.podinfo.svc.cluster.local\n port:\n number: 9898\n```\n\n

    \n
    ", - "x": 630, - "y": 480, + "x": 420, + "y": 540, "wires": [ [ "339481fd79e107a6" @@ -435,12 +436,12 @@ }, { "id": "339481fd79e107a6", - "type": "comment", + "type": "task", "z": "c86af370eff94afa", "name": "Deploy podinfo as a Flux HelmRelease", "info": "# Flux helm release reconciliation\n\"Big Bang follows a GitOps approach to configuration management, using Flux v2 to reconcile Git with the cluster. Environments (e.g. dev, prod) and packages (e.g. istio) can be fully configured to suit the deployment needs.\"\n\n## Prerequisites\n- Remove all prior content for podinfo from your cluster. \n\n## Recommended Reading\n- https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/master/README.md\n\n## Objective\nFollowing the umbrella structure in the Big Bang chart, we can create the templates under a `podinfo` directory (under the templates dir).\n\nNote: there are many ways to accomplish this task - the podinfo directory under the templates directory most-accurately represents how the umbrella structure works.\n\n### Minimum Requirements\n- namespace resource\n- git repository resource\n- helm release resource\n- virtual service resource\n\nThis would meet the bare minimum requirements.\n\n### Additional Content\n- values file (a helper generates a secret resource from this)\n\n## Success Criteria\nIf we run the `helm upgrade` command against the modified Big Bang helm chart, it should deploy the podinfo chart (if properly enabled) with the ability to access/resolve the application through the `Ingress Gateway`.", - "x": 630, - "y": 550, + "x": 420, + "y": 610, "wires": [ [ "0f9889d8e9cb7edb" @@ -449,36 +450,36 @@ }, { "id": "0f9889d8e9cb7edb", - "type": "comment", + "type": "guide", "z": "c86af370eff94afa", "name": "Play DOOM using ZARF", "info": "# Zarf!\n\nZarf is a Defense Unicorns developed tool - please read about it below!\n\n## Recommended Reading\n- https://github.com/defenseunicorns/zarf\n\n\n## Objective\nUsing Zarf, we will follow the guide below in order to play DOOM.\n\nhttps://github.com/defenseunicorns/zarf/tree/master/examples/game\n\n## Success Criteria\nPackaging, Deployment and execution of playing DOOM from within the browser.", - "x": 630, - "y": 620, + "x": 420, + "y": 680, "wires": [ [] ] }, { "id": "1458b18889c13942", - "type": "comment", + "type": "guide", "z": "c86af370eff94afa", "name": "Deploy and Access the Kubernetes Dashboard", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 240, - "y": 130, + "x": 800, + "y": 190, "wires": [ [] ] }, { "id": "6d2d525b92151db2", - "type": "comment", + "type": "resource", "z": "c86af370eff94afa", "name": "Kustomize", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 740, - "y": 40, + "x": 230, + "y": 120, "wires": [ [ "691424f6cfd58372" @@ -487,12 +488,12 @@ }, { "id": "a25127e60aa3afac", - "type": "comment", + "type": "resource", "z": "c86af370eff94afa", "name": "Helm", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 910, - "y": 200, + "x": 150, + "y": 260, "wires": [ [ "52eb692e4c51108f" @@ -501,12 +502,12 @@ }, { "id": "bd30d9f248bb244a", - "type": "comment", + "type": "resource", "z": "c86af370eff94afa", "name": "Big Bang", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 910, - "y": 270, + "x": 150, + "y": 330, "wires": [ [ "5361df412a99db12" @@ -515,12 +516,12 @@ }, { "id": "f17eae05b57f6a2e", - "type": "comment", + "type": "resource", "z": "c86af370eff94afa", "name": "Istio", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 910, - "y": 410, + "x": 150, + "y": 470, "wires": [ [ "9b8fdffdc9b5cc53" @@ -529,12 +530,12 @@ }, { "id": "3a185c296c95de08", - "type": "comment", + "type": "resource", "z": "c86af370eff94afa", "name": "Flux", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 910, - "y": 480, + "x": 150, + "y": 540, "wires": [ [ "339481fd79e107a6" @@ -543,12 +544,12 @@ }, { "id": "14c8378318de0a59", - "type": "comment", + "type": "resource", "z": "c86af370eff94afa", "name": "Traefix Ingress", "info": "# Description\n\n# Resources\n\n# Unicorn SME(s)\n", - "x": 910, - "y": 130, + "x": 150, + "y": 190, "wires": [ [ "2d66fece0a8f5fdf" diff --git a/packages/node_modules/@node-red/nodes/core/common/task.html b/packages/node_modules/@node-red/nodes/core/common/task.html index c1f8ebdac..c59c309b8 100644 --- a/packages/node_modules/@node-red/nodes/core/common/task.html +++ b/packages/node_modules/@node-red/nodes/core/common/task.html @@ -17,7 +17,7 @@