From b1daa8932a2f097e643042afdfaea765b954666c Mon Sep 17 00:00:00 2001 From: "andrew.greene" Date: Wed, 8 Dec 2021 18:01:31 -0700 Subject: [PATCH] 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() {}); -});