From 491812fac5d524692bcb489b6e20890eb533f1ce Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 2 Mar 2020 05:07:48 +0000 Subject: [PATCH 01/34] Fix XPath in UI tests --- test/editor/pageobjects/nodes/core/function/15-change_page.js | 2 +- test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js | 2 +- test/editor/pageobjects/nodes/core/parsers/70-XML_page.js | 2 +- test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/editor/pageobjects/nodes/core/function/15-change_page.js b/test/editor/pageobjects/nodes/core/function/15-change_page.js index 59ac4f2f1..fab408718 100644 --- a/test/editor/pageobjects/nodes/core/function/15-change_page.js +++ b/test/editor/pageobjects/nodes/core/function/15-change_page.js @@ -85,7 +85,7 @@ changeNode.prototype.ruleMove = function (p, to, index) { } changeNode.prototype.addRule = function () { - browser.clickWithWait('//*[@id="dialog-form"]/div[5]/div/a'); + browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a'); } module.exports = changeNode; diff --git a/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js b/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js index 875c3b013..e0b31dd36 100644 --- a/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js +++ b/test/editor/pageobjects/nodes/core/parsers/70-JSON_page.js @@ -29,7 +29,7 @@ jsonNode.prototype.setAction = function (action) { } jsonNode.prototype.setProperty = function (property) { - browser.setValue('//*[@id="dialog-form"]/div[4]/div/div[1]/input', 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 index 2a8be7d95..696ec59cb 100644 --- a/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js +++ b/test/editor/pageobjects/nodes/core/parsers/70-XML_page.js @@ -29,7 +29,7 @@ xmlNode.prototype.setAction = function (action) { } xmlNode.prototype.setProperty = function (property) { - browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', 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 index 16e4a678d..1002f3eb4 100644 --- a/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js +++ b/test/editor/pageobjects/nodes/core/parsers/70-YAML_page.js @@ -29,7 +29,7 @@ yamlNode.prototype.setAction = function (action) { } yamlNode.prototype.setProperty = function (property) { - browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', property); + browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property); } module.exports = yamlNode; From b78ef006ec6798dd04d6e82d7af5cf58c40cc0b1 Mon Sep 17 00:00:00 2001 From: tilleul Date: Mon, 9 Mar 2020 11:22:20 +0100 Subject: [PATCH 02/34] Support for context stores using JSONata and evaluateNodeProperty() The function prepareJSONataExpression() does not take context store into account. This causes problems when using typedInput fields and getting the value of a property based on its type using evaluateNodeProperty(). --- packages/node_modules/@node-red/util/lib/util.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/util/lib/util.js b/packages/node_modules/@node-red/util/lib/util.js index 45a33be14..cd273dfae 100644 --- a/packages/node_modules/@node-red/util/lib/util.js +++ b/packages/node_modules/@node-red/util/lib/util.js @@ -558,11 +558,11 @@ function evaluateNodeProperty(value, type, node, msg, callback) { */ function prepareJSONataExpression(value,node) { var expr = jsonata(value); - expr.assign('flowContext',function(val) { - return node.context().flow.get(val); + expr.assign('flowContext',function(val, store) { + return node.context().flow.get(val, store); }); - expr.assign('globalContext',function(val) { - return node.context().global.get(val); + expr.assign('globalContext',function(val, store) { + return node.context().global.get(val, store); }); expr.assign('env', function(name) { var val = getSetting(node, name); From 369c5754f2ce77251ada0067675d04d47b5a5840 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 13 Mar 2020 16:26:48 +0900 Subject: [PATCH 03/34] Add Japanese translation to node property of websocket node --- .../@node-red/nodes/core/network/22-websocket.html | 4 ++-- .../node_modules/@node-red/nodes/locales/en-US/messages.json | 2 +- .../node_modules/@node-red/nodes/locales/ja/messages.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 index 4e62cb95e..ad3fb600f 100644 --- a/packages/node_modules/@node-red/nodes/core/network/22-websocket.html +++ b/packages/node_modules/@node-red/nodes/core/network/22-websocket.html @@ -163,7 +163,7 @@ if (root === "") { $("#node-config-ws-tip").hide(); } else { - $("#node-config-ws-path").html(root); + $("#node-config-ws-path").html(RED._("node-red:websocket.tip.path2", { path: root })); $("#node-config-ws-tip").show(); } } @@ -235,7 +235,7 @@
-

.

+

diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index e1d7c6368..bf476ab44 100755 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -455,7 +455,7 @@ "message": "entire message", "tip": { "path1": "By default, payload will contain the data to be sent over, or received from a websocket. The listener can be configured to send or receive the entire message object as a JSON formatted string.", - "path2": "This path will be relative to ", + "path2": "This path will be relative to __path__.", "url1": "URL should use ws:// or wss:// scheme and point to an existing websocket listener.", "url2": "By default, payload will contain the data to be sent over, or received from a websocket. The client can be configured to send or receive the entire message object as a JSON formatted string." }, diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json index adc033390..4e728690b 100755 --- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json @@ -455,7 +455,7 @@ "message": "メッセージ全体を送信/受信", "tip": { "path1": "標準では payload がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。", - "path2": "This path will be relative to ", + "path2": "このパスは __path__ の相対パスになります。", "url1": "URLには ws:// または wss:// スキーマを使用して、存在するwebsocketリスナを設定してください。", "url2": "標準では payload がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。" }, From fd2213232c936f93cdec17fabde6c33dd527f5c0 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 13 Mar 2020 16:29:16 +0900 Subject: [PATCH 04/34] Update message catalogs for other languages --- packages/node_modules/@node-red/nodes/locales/de/messages.json | 2 +- packages/node_modules/@node-red/nodes/locales/ko/messages.json | 2 +- .../node_modules/@node-red/nodes/locales/zh-CN/messages.json | 2 +- .../node_modules/@node-red/nodes/locales/zh-TW/messages.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/locales/de/messages.json b/packages/node_modules/@node-red/nodes/locales/de/messages.json index c36c309f3..58fce3a92 100755 --- a/packages/node_modules/@node-red/nodes/locales/de/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/de/messages.json @@ -397,7 +397,7 @@ "message" : "gesamte Nachricht", "tip" : { "path1" : "Standardmäßig enthält Nutzdaten die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Listener kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt.", - "path2" : "Dieser Pfad ist relativ zu ", + "path2" : "Dieser Pfad ist relativ zu __path__.", "url1" : "URL sollte ws: / & #47; oder wss: / & #47; Schema verwenden und auf einen vorhandenen Websocket-Listener verweisen.", "url2" : "Standardmäßig enthält Nutzdaten die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Client kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt." }, diff --git a/packages/node_modules/@node-red/nodes/locales/ko/messages.json b/packages/node_modules/@node-red/nodes/locales/ko/messages.json index d87b2c0f5..8e3a4f325 100755 --- a/packages/node_modules/@node-red/nodes/locales/ko/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/ko/messages.json @@ -446,7 +446,7 @@ "message": "메세지 전체를 송신/수신", "tip": { "path1": "표준으로는 payload 가 websocket에서 송신, 수신된 데이터를 기다립니다. 클라이언트는 JSON형식의 문자열로 메세지전체를 송신, 수신하도록 설정할 수 있습니다.", - "path2": "This path will be relative to ", + "path2": "This path will be relative to __path__.", "url1": "URL에는 ws:// 또는 wss:// 스키마를 사용하여, 존재하는 websocket리스너를 설정해 주세요.", "url2": "표준으로는 payload 가 websocket에서 송신,수신될 데이터를 기다립니다.클라이언트는 JSON형식의 문자열로 메세지전체를 송신, 수신하도록 설정할 수 있습니다." }, diff --git a/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json b/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json index 4fc5dc4d4..934cc734d 100644 --- a/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/zh-CN/messages.json @@ -455,7 +455,7 @@ "message": "完整信息", "tip": { "path1": "默认情况下,payload将包含要发送或从Websocket接收的数据。侦听器可以配置为以JSON格式的字符串发送或接收整个消息对象.", - "path2": "这条路径将相对于 ", + "path2": "这条路径将相对于 __path__.", "url1": "URL 应该使用ws://或者wss://方案并指向现有的websocket侦听器.", "url2": "默认情况下,payload 将包含要发送或从Websocket接收的数据。可以将客户端配置为以JSON格式的字符串发送或接收整个消息对象." }, diff --git a/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json b/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json index 7ece3333d..5368bb993 100644 --- a/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/zh-TW/messages.json @@ -455,7 +455,7 @@ "message": "完整資訊", "tip": { "path1": "預設情況下,payload將包含要發送或從Websocket接收的資料。偵聽器可以配置為以JSON格式的字串發送或接收整個消息物件.", - "path2": "這條路徑將相對於 ", + "path2": "這條路徑將相對於 __path__.", "url1": "URL 應該使用ws://或者wss://方案並指向現有的websocket監聽器.", "url2": "預設情況下,payload 將包含要發送或從Websocket接收的資料。可以將使用者端配置為以JSON格式的字串發送或接收整個消息物件." }, From 72126730efb4439d474f61d6077903f9989bfc0e Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 13 Mar 2020 16:32:00 +0900 Subject: [PATCH 05/34] Remove unnecessary code for node property of websocket node in the German language --- .../locales/de/network/22-websocket.html | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/locales/de/network/22-websocket.html b/packages/node_modules/@node-red/nodes/locales/de/network/22-websocket.html index 5aa5cf884..26cc078ae 100755 --- a/packages/node_modules/@node-red/nodes/locales/de/network/22-websocket.html +++ b/packages/node_modules/@node-red/nodes/locales/de/network/22-websocket.html @@ -37,30 +37,6 @@

Dieser Konfigurations-Node erstellt einen WebSocket Server-Endpunkt unter Verwendung des angegebenen Pfades.

- - - From 9ba9998bd6fd87443d849a17fddb6df8cff66275 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 13 Mar 2020 11:26:49 +0000 Subject: [PATCH 06/34] make exec node logging consistent with itself. (only be verbose when in verbose mode) --- packages/node_modules/@node-red/nodes/core/function/90-exec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index a5132230e..f5e7edd87 100644 --- a/packages/node_modules/@node-red/nodes/core/function/90-exec.js +++ b/packages/node_modules/@node-red/nodes/core/function/90-exec.js @@ -145,7 +145,7 @@ module.exports = function(RED) { if (error.signal) { msg3.payload.signal = error.signal; } if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); } else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); } - node.log('error:' + error); + if (RED.settings.verbose) { node.log('error:' + error); } } else if (node.oldrc === "false") { msg3 = RED.util.cloneMessage(msg); From b165129388f37f2f8b873016c1cc346b9aaf0185 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 13 Mar 2020 11:28:19 +0000 Subject: [PATCH 07/34] Remove old leagcy wording from file node info to stop confusing users. --- .../@node-red/nodes/core/storage/10-file.html | 4 ++-- .../nodes/locales/en-US/storage/10-file.html | 15 ++------------- 2 files changed, 4 insertions(+), 15 deletions(-) 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 index d20ad75c5..5487920b8 100755 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/core/storage/10-file.html @@ -1,5 +1,5 @@ - - - From 09d55a0cbdff545f59138c232163283dfe0fde65 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 13 Mar 2020 11:33:37 +0000 Subject: [PATCH 08/34] remove unneeded title line from file info text --- .../@node-red/nodes/locales/en-US/storage/10-file.html | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/storage/10-file.html b/packages/node_modules/@node-red/nodes/locales/en-US/storage/10-file.html index f25ce0292..fe2bbc324 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/locales/en-US/storage/10-file.html @@ -49,7 +49,6 @@
The contents of the file as either a string or binary buffer.
filename string
If not configured in the node, this optional property sets the name of the file to be read.
-
error object

Details

The filename should be an absolute path, otherwise it will be relative to From a9508a2c0402e055011d48d28e5df3bb353269eb Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 13 Mar 2020 21:31:16 +0900 Subject: [PATCH 09/34] Remove old leagcy wording from file node info (Japanese) --- .../@node-red/nodes/locales/ja/storage/10-file.html | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/locales/ja/storage/10-file.html b/packages/node_modules/@node-red/nodes/locales/ja/storage/10-file.html index 2df174104..21d4af9be 100644 --- a/packages/node_modules/@node-red/nodes/locales/ja/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/locales/ja/storage/10-file.html @@ -14,7 +14,7 @@ limitations under the License. --> - - From c700d5c922af369680c27248c8da2863c2c75e63 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 13 Mar 2020 21:38:23 +0900 Subject: [PATCH 10/34] Remove old leagcy wording from file node info (Chinese) --- .../@node-red/nodes/locales/zh-CN/storage/10-file.html | 8 ++------ .../@node-red/nodes/locales/zh-TW/storage/10-file.html | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/locales/zh-CN/storage/10-file.html b/packages/node_modules/@node-red/nodes/locales/zh-CN/storage/10-file.html index 4ec78cdb4..eb38a5235 100644 --- a/packages/node_modules/@node-red/nodes/locales/zh-CN/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/locales/zh-CN/storage/10-file.html @@ -14,7 +14,7 @@ limitations under the License. --> - - diff --git a/packages/node_modules/@node-red/nodes/locales/zh-TW/storage/10-file.html b/packages/node_modules/@node-red/nodes/locales/zh-TW/storage/10-file.html index 03705ea5c..ce2531137 100644 --- a/packages/node_modules/@node-red/nodes/locales/zh-TW/storage/10-file.html +++ b/packages/node_modules/@node-red/nodes/locales/zh-TW/storage/10-file.html @@ -14,7 +14,7 @@ limitations under the License. --> - - From 4c78f06c2b639f8f7eb9edcfe3f31a7d296cce2b Mon Sep 17 00:00:00 2001 From: Mauricio Bonani Date: Fri, 13 Mar 2020 08:44:56 -0400 Subject: [PATCH 11/34] Fix paletteCategories order --- packages/node_modules/node-red/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/node-red/settings.js b/packages/node_modules/node-red/settings.js index 960cb6d17..c5e4355e1 100644 --- a/packages/node_modules/node-red/settings.js +++ b/packages/node_modules/node-red/settings.js @@ -243,7 +243,7 @@ module.exports = { // palette. If a node's category is not in the list, the category will get // added to the end of the palette. // If not set, the following default order is used: - //paletteCategories: ['subflows','flow','input','output','function','parser','social','mobile','storage','analysis','advanced'], + //paletteCategories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'], // Configure the logging output logging: { From 20f97d0d136310a994b185b0de13ea6da88b9904 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 13 Mar 2020 13:09:47 +0000 Subject: [PATCH 12/34] Add better handling of host-key-verify error with projects --- .../editor-client/locales/en-US/editor.json | 3 +- .../src/js/ui/projects/projects.js | 171 ++++++++++-------- .../localfilesystem/projects/git/index.js | 6 +- 3 files changed, 101 insertions(+), 79 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index a5f232aa6..ed4d4f9a6 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -977,7 +977,8 @@ "passphrase": "Passphrase", "retry": "Retry", "update-failed": "Failed to update auth", - "unhandled": "Unhandled error response" + "unhandled": "Unhandled error response", + "host-key-verify-failed": "

Host key verification failed.

The repository host key could not be verified. Please update your known_hosts file and try again." }, "create-branch-list": { "invalid": "Invalid branch", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js index 0c297c82a..da54a81c8 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js @@ -1939,100 +1939,121 @@ RED.projects = (function() { } }).fail(function(xhr,textStatus,err) { var responses; + if (options.responses && options.responses[xhr.status]) { responses = options.responses[xhr.status]; if (typeof responses === 'function') { resultCallback = responses; resultCallbackArgs = {error:responses.statusText}; return; - } else if (options.handleAuthFail !== false && xhr.responseJSON.code === 'git_auth_failed') { - var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch; + } else if (options.handleAuthFail !== false && (xhr.responseJSON.code === 'git_auth_failed' || xhr.responseJSON.code === 'git_host_key_verification_failed')) { + if (xhr.responseJSON.code === 'git_auth_failed') { + var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch; - var message = $('

'+ + var message = $('
'+ '
'+RED._("projects.send-req.auth-req")+':
'+ '
'+url+'
'+ '
'); - var isSSH = false; - if (/^https?:\/\//.test(url)) { - $('
'+ - '
').appendTo(message); - } else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) { - isSSH = true; - var row = $('
').appendTo(message); - $('').appendTo(row); - var projectRepoSSHKeySelect = $('
'+ + '
').appendTo(message); + } else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) { + isSSH = true; + var row = $('
').appendTo(message); + $('').appendTo(row); + var projectRepoSSHKeySelect = $('').appendTo(row); - } + row = $('
').appendTo(message); + $('').appendTo(row); + $('').appendTo(row); + } - var notification = RED.notify(message,{ - type:"error", - fixed: true, - modal: true, - buttons: [ - { - //id: "node-dialog-delete", - //class: 'leftButton', - text: RED._("common.label.cancel"), - click: function() { - notification.close(); - } - },{ - text: ' ' +RED._("projects.send-req.retry") +'', - click: function() { - body = body || {}; - var authBody = {}; - if (isSSH) { - authBody.keyFile = $('#projects-user-auth-key').val(); - authBody.passphrase = $('#projects-user-auth-passphrase').val(); - } else { - authBody.username = $('#projects-user-auth-username').val(); - authBody.password = $('#projects-user-auth-password').val(); + var notification = RED.notify(message,{ + type:"error", + fixed: true, + modal: true, + buttons: [ + { + //id: "node-dialog-delete", + //class: 'leftButton', + text: RED._("common.label.cancel"), + click: function() { + notification.close(); } - var done = function(err) { - if (err) { - console.log(RED._("projects.send-req.update-failed")); - console.log(err); + },{ + text: ' ' +RED._("projects.send-req.retry") +'', + click: function() { + body = body || {}; + var authBody = {}; + if (isSSH) { + authBody.keyFile = $('#projects-user-auth-key').val(); + authBody.passphrase = $('#projects-user-auth-passphrase').val(); } else { - sendRequest(options,body); - notification.close(); + authBody.username = $('#projects-user-auth-username').val(); + authBody.password = $('#projects-user-auth-password').val(); } + var done = function(err) { + if (err) { + console.log(RED._("projects.send-req.update-failed")); + console.log(err); + } else { + sendRequest(options,body); + notification.close(); + } - } - sendRequest({ - url: "projects/"+activeProject.name+"/remotes/"+(xhr.responseJSON.remote||options.remote||'origin'), - type: "PUT", - responses: { - 0: function(error) { - done(error,null); - }, - 200: function(data) { - done(null,data); - }, - 400: { - 'unexpected_error': function(error) { - done(error,null); - } - }, } - },{auth:authBody}); + sendRequest({ + url: "projects/"+activeProject.name+"/remotes/"+(xhr.responseJSON.remote||options.remote||'origin'), + type: "PUT", + responses: { + 0: function(error) { + done(error,null); + }, + 200: function(data) { + done(null,data); + }, + 400: { + 'unexpected_error': function(error) { + done(error,null); + } + }, + } + },{auth:authBody}); + } } - } - ] - }); - return; + ] + }); + return; + } else if (xhr.responseJSON.code === 'git_host_key_verification_failed') { + var message = $('
'+ + '
'+RED._("projects.send-req.host-key-verify-failed")+'
'+ + '
'); + var notification = RED.notify(message,{ + type:"error", + fixed: true, + modal: true, + buttons: [ + { + text: RED._("common.label.close"), + click: function() { + notification.close(); + } + } + ] + }); + return; + } } else if (responses[xhr.responseJSON.code]) { resultCallback = responses[xhr.responseJSON.code]; resultCallbackArgs = xhr.responseJSON; diff --git a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js index b9f114698..d812c05f2 100644 --- a/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js +++ b/packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/index.js @@ -41,6 +41,9 @@ function runGitCommand(args,cwd,env,emit) { err.code = "git_connection_failed"; } else if (/Connection timed out/i.test(stderr)) { err.code = "git_connection_failed"; + } else if(/Host key verification failed/i.test(stderr)) { + // TODO: handle host key verification errors separately + err.code = "git_host_key_verification_failed"; } else if (/fatal: could not read/i.test(stderr)) { // Username/Password err.code = "git_auth_failed"; @@ -48,9 +51,6 @@ function runGitCommand(args,cwd,env,emit) { err.code = "git_auth_failed"; } else if(/Permission denied \(publickey\)/i.test(stderr)) { err.code = "git_auth_failed"; - } else if(/Host key verification failed/i.test(stderr)) { - // TODO: handle host key verification errors separately - err.code = "git_auth_failed"; } else if (/commit your changes or stash/i.test(stderr)) { err.code = "git_local_overwrite"; } else if (/CONFLICT/.test(err.stdout)) { From 421b5846f2f93b24699e86d001d93b4520e89f17 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 13 Mar 2020 22:20:16 +0900 Subject: [PATCH 13/34] Add page objects for UI testing (#2501) * Update page object of change node * Support multiple node outputs in UI testing * Add page object of switch node * Add page objects of trigger and exec nodes * Remove unnecessary code * Update page object of trigger node to select time unit * Add page objects of websocket nodes * Support boolean as value in selectWithWait() * Update page object of split node * Merge page objects of mqtt nodes to make them same as original mqtt node file path --- .../editor/pageobjects/editor/palette_page.js | 5 + .../nodes/core/common/21-debug_page.js | 2 - .../nodes/core/function/10-switch_page.js | 234 ++++++++++++++++++ .../nodes/core/function/15-change_page.js | 67 ++++- .../nodes/core/function/89-delay_page.js | 2 - .../nodes/core/function/89-trigger_page.js | 83 +++++++ .../90-exec_page.js} | 16 +- ...{10-mqttconfig_page.js => 10-mqtt_page.js} | 52 +++- .../nodes/core/network/10-mqttout_page.js | 35 --- .../nodes/core/network/22-websocket_page.js | 93 +++++++ .../nodes/core/sequence/17-split_page.js | 16 +- test/editor/pageobjects/nodes/node_page.js | 7 +- .../pageobjects/nodes/nodefactory_page.js | 18 +- test/editor/pageobjects/util/util_page.js | 2 +- .../scenario/cookbook_messages_uispec.js | 2 +- .../specs/scenario/cookbook_mqtt_uispec.js | 7 +- 16 files changed, 558 insertions(+), 83 deletions(-) create mode 100644 test/editor/pageobjects/nodes/core/function/10-switch_page.js create mode 100644 test/editor/pageobjects/nodes/core/function/89-trigger_page.js rename test/editor/pageobjects/nodes/core/{network/10-mqttin_page.js => function/90-exec_page.js} (66%) rename test/editor/pageobjects/nodes/core/network/{10-mqttconfig_page.js => 10-mqtt_page.js} (52%) delete mode 100644 test/editor/pageobjects/nodes/core/network/10-mqttout_page.js create mode 100644 test/editor/pageobjects/nodes/core/network/22-websocket_page.js diff --git a/test/editor/pageobjects/editor/palette_page.js b/test/editor/pageobjects/editor/palette_page.js index 6af0b5271..3b484a58c 100644 --- a/test/editor/pageobjects/editor/palette_page.js +++ b/test/editor/pageobjects/editor/palette_page.js @@ -24,16 +24,21 @@ var idMap = { "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']", diff --git a/test/editor/pageobjects/nodes/core/common/21-debug_page.js b/test/editor/pageobjects/nodes/core/common/21-debug_page.js index 56854b195..990ed6cd9 100644 --- a/test/editor/pageobjects/nodes/core/common/21-debug_page.js +++ b/test/editor/pageobjects/nodes/core/common/21-debug_page.js @@ -18,8 +18,6 @@ var util = require("util"); var nodePage = require("../../node_page"); -var keyPage = require("../../../util/key_page"); - function debugNode(id) { nodePage.call(this, id); } diff --git a/test/editor/pageobjects/nodes/core/function/10-switch_page.js b/test/editor/pageobjects/nodes/core/function/10-switch_page.js new file mode 100644 index 000000000..a04014063 --- /dev/null +++ b/test/editor/pageobjects/nodes/core/function/10-switch_page.js @@ -0,0 +1,234 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 index fab408718..eb26f48aa 100644 --- a/test/editor/pageobjects/nodes/core/function/15-change_page.js +++ b/test/editor/pageobjects/nodes/core/function/15-change_page.js @@ -51,37 +51,78 @@ function setT(t, index) { // 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); + setT('set', index); if (pt) { browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); - var num = 5 * (index - 1) + 1; - var ptXPath = '//div[contains(@class, "red-ui-typedInput-options")][' + num + ']/a[' + ptType[pt] + ']'; - browser.clickWithWait(ptXPath); + 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]'); - var num = 5 * (index - 1) + 2; - var totXPath = '//div[contains(@class, "red-ui-typedInput-options")][' + num + ']/a[' + totType[tot] + ']'; - browser.clickWithWait(totXPath); + 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.ruleDelete = function (index) { +changeNode.prototype.ruleChange = function (p, pt, from, fromt, to, tot, index) { index = index || 1; - setT("delete", index); + 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.ruleMove = function (p, to, index) { +changeNode.prototype.ruleDelete = function (p, pt, index) { index = index || 1; - setT("move", index); - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p); - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/div/input', to); + 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 () { diff --git a/test/editor/pageobjects/nodes/core/function/89-delay_page.js b/test/editor/pageobjects/nodes/core/function/89-delay_page.js index a4e2197e4..3604beb67 100644 --- a/test/editor/pageobjects/nodes/core/function/89-delay_page.js +++ b/test/editor/pageobjects/nodes/core/function/89-delay_page.js @@ -18,8 +18,6 @@ var util = require("util"); var nodePage = require("../../node_page"); -var keyPage = require("../../../util/key_page"); - function delayNode(id) { nodePage.call(this, id); } diff --git a/test/editor/pageobjects/nodes/core/function/89-trigger_page.js b/test/editor/pageobjects/nodes/core/function/89-trigger_page.js new file mode 100644 index 000000000..5d24de380 --- /dev/null +++ b/test/editor/pageobjects/nodes/core/function/89-trigger_page.js @@ -0,0 +1,83 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/network/10-mqttin_page.js b/test/editor/pageobjects/nodes/core/function/90-exec_page.js similarity index 66% rename from test/editor/pageobjects/nodes/core/network/10-mqttin_page.js rename to test/editor/pageobjects/nodes/core/function/90-exec_page.js index 31b909116..69b8b6c9a 100644 --- a/test/editor/pageobjects/nodes/core/network/10-mqttin_page.js +++ b/test/editor/pageobjects/nodes/core/function/90-exec_page.js @@ -18,18 +18,20 @@ var util = require("util"); var nodePage = require("../../node_page"); -function mqttInNode(id) { +function execNode(id) { nodePage.call(this, id); } -util.inherits(mqttInNode, nodePage); +util.inherits(execNode, nodePage); -mqttInNode.prototype.setTopic = function (topic) { - browser.setValue('#node-input-topic', topic); +execNode.prototype.setCommand = function (command) { + browser.setValue('//*[@id="node-input-command"]', command); } -mqttInNode.prototype.setQoS = function (qos) { - browser.selectWithWait('#node-input-qos', qos); +execNode.prototype.setAppend = function (checkbox) { + if (browser.isSelected('#node-input-addpay') !== checkbox) { + browser.click('#node-input-addpay'); + } } -module.exports = mqttInNode; +module.exports = execNode; diff --git a/test/editor/pageobjects/nodes/core/network/10-mqttconfig_page.js b/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js similarity index 52% rename from test/editor/pageobjects/nodes/core/network/10-mqttconfig_page.js rename to test/editor/pageobjects/nodes/core/network/10-mqtt_page.js index c7cdc90c5..4bdd92336 100644 --- a/test/editor/pageobjects/nodes/core/network/10-mqttconfig_page.js +++ b/test/editor/pageobjects/nodes/core/network/10-mqtt_page.js @@ -14,27 +14,61 @@ * limitations under the License. **/ -function setServer(broker, port) { +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); -} +}; -function clickOk() { +mqttBrokerNode.clickOk = function () { browser.clickWithWait('#node-config-dialog-ok'); // Wait until an config dialog closes. browser.waitForVisible('#node-config-dialog-ok', 10000, true); -} +}; -function edit() { +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); } -module.exports = { - setServer: setServer, - clickOk: clickOk, - edit: edit +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/10-mqttout_page.js b/test/editor/pageobjects/nodes/core/network/10-mqttout_page.js deleted file mode 100644 index 783d87b55..000000000 --- a/test/editor/pageobjects/nodes/core/network/10-mqttout_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 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); -} - -module.exports = mqttOutNode; \ No newline at end of file diff --git a/test/editor/pageobjects/nodes/core/network/22-websocket_page.js b/test/editor/pageobjects/nodes/core/network/22-websocket_page.js new file mode 100644 index 000000000..8f7dc261e --- /dev/null +++ b/test/editor/pageobjects/nodes/core/network/22-websocket_page.js @@ -0,0 +1,93 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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/sequence/17-split_page.js b/test/editor/pageobjects/nodes/core/sequence/17-split_page.js index 3c8c70aa5..8fc32ab1e 100644 --- a/test/editor/pageobjects/nodes/core/sequence/17-split_page.js +++ b/test/editor/pageobjects/nodes/core/sequence/17-split_page.js @@ -24,7 +24,19 @@ function splitNode(id) { util.inherits(splitNode, nodePage); -module.exports = splitNode; +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); @@ -32,4 +44,4 @@ function joinNode(id) { util.inherits(joinNode, nodePage); -module.exports = joinNode; +module.exports.joinNode = joinNode; diff --git a/test/editor/pageobjects/nodes/node_page.js b/test/editor/pageobjects/nodes/node_page.js index 5250250e7..03e734cab 100644 --- a/test/editor/pageobjects/nodes/node_page.js +++ b/test/editor/pageobjects/nodes/node_page.js @@ -35,10 +35,11 @@ Node.prototype.clickOk = function () { browser.pause(50); } -Node.prototype.connect = function (targetNode) { - var outputPort = this.id + '/*[@class="red-ui-flow-port-output"]'; +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) + browser.dragAndDrop(outputPort, inputPort); } Node.prototype.clickLeftButton = function () { diff --git a/test/editor/pageobjects/nodes/nodefactory_page.js b/test/editor/pageobjects/nodes/nodefactory_page.js index 17949e313..008ecc625 100644 --- a/test/editor/pageobjects/nodes/nodefactory_page.js +++ b/test/editor/pageobjects/nodes/nodefactory_page.js @@ -21,17 +21,22 @@ 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 mqttInNode = require('./core/network/10-mqttin_page'); -var mqttOutNode = require('./core/network/10-mqttout_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 splitNode = require('./core/sequence/17-split_page'); -var joinNode = require('./core/sequence/17-split_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'); @@ -50,16 +55,21 @@ var nodeCatalog = { "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, diff --git a/test/editor/pageobjects/util/util_page.js b/test/editor/pageobjects/util/util_page.js index 02508c831..3a764eb93 100644 --- a/test/editor/pageobjects/util/util_page.js +++ b/test/editor/pageobjects/util/util_page.js @@ -70,7 +70,7 @@ function init() { var ret = repeatUntilSuccess(function(args) { return browser.selectByValue(args[0], args[1]); - }, [selector, value]); + }, [selector, value.toString()]); return ret; } catch (e) { console.trace(); diff --git a/test/editor/specs/scenario/cookbook_messages_uispec.js b/test/editor/specs/scenario/cookbook_messages_uispec.js index a9465e5d8..78facbcda 100644 --- a/test/editor/specs/scenario/cookbook_messages_uispec.js +++ b/test/editor/specs/scenario/cookbook_messages_uispec.js @@ -88,7 +88,7 @@ describe('cookbook', function () { injectNode.clickOk(); changeNode.edit(); - changeNode.ruleMove('topic', 'payload'); + changeNode.ruleMove('topic', 'msg', 'payload', 'msg'); changeNode.clickOk(); injectNode.connect(changeNode); diff --git a/test/editor/specs/scenario/cookbook_mqtt_uispec.js b/test/editor/specs/scenario/cookbook_mqtt_uispec.js index 655074750..b68170eb5 100644 --- a/test/editor/specs/scenario/cookbook_mqtt_uispec.js +++ b/test/editor/specs/scenario/cookbook_mqtt_uispec.js @@ -22,7 +22,6 @@ 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 mqttConfig = require('../../pageobjects/nodes/core/network/10-mqttconfig_page.js'); var httpNodeRoot = "/api"; @@ -73,9 +72,9 @@ describe('cookbook', function () { var mqttOutNode = workspace.addNode("mqttOut"); mqttOutNode.edit(); - mqttConfig.edit(); - mqttConfig.setServer("localhost", moscaSettings.port); - mqttConfig.clickOk(); + mqttOutNode.mqttBrokerNode.edit(); + mqttOutNode.mqttBrokerNode.setServer("localhost", moscaSettings.port); + mqttOutNode.mqttBrokerNode.clickOk(); mqttOutNode.clickOk(); workspace.deploy(); From 43258ee816f1cf354eb2db75c215bb9e05245f49 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sun, 15 Mar 2020 15:11:19 +0000 Subject: [PATCH 14/34] Trigger node - reset default timeout value when switcing away from wait for reset --- .../@node-red/nodes/core/function/89-trigger.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 index 28bf0cdeb..d20391b59 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html +++ b/packages/node_modules/@node-red/nodes/core/function/89-trigger.html @@ -14,11 +14,11 @@ limitations under the License. --> - From 85a1f59a93664e317934b3beac7f9de60f00b1c9 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sun, 15 Mar 2020 16:43:32 +0000 Subject: [PATCH 15/34] Fix join to not crash on appending invalid tyoes to buffer. Add extra info to clarify use of complete to Close #2505 --- .../@node-red/nodes/core/sequence/17-split.js | 8 +++++++- .../@node-red/nodes/locales/en-US/messages.json | 3 ++- .../@node-red/nodes/locales/en-US/sequence/17-split.html | 7 ++++--- 3 files changed, 13 insertions(+), 5 deletions(-) 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 index fc6042625..ced637555 100644 --- a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js +++ b/packages/node_modules/@node-red/nodes/core/sequence/17-split.js @@ -630,7 +630,13 @@ module.exports = function(RED) { var group = inflight[partId]; if (payloadType === 'buffer') { if (property !== undefined) { - inflight[partId].bufferLen += property.length; + if (Buffer.isBuffer(property) || (typeof property === "string") || Array.isArray(property)) { + inflight[partId].bufferLen += property.length; + } + else { + node.error(RED._("join.errors.invalid-type",{error:(typeof property)}),msg); + return; + } } } if (payloadType === 'object') { diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index e1d7c6368..24c80003b 100755 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -894,7 +894,8 @@ "fixup": "Fix-up exp" }, "errors": { - "invalid-expr": "Invalid JSONata expression: __error__" + "invalid-expr": "Invalid JSONata expression: __error__", + "invalid-type": "Cannot join __error__ to buffer" } }, "sort" : { diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/sequence/17-split.html b/packages/node_modules/@node-red/nodes/locales/en-US/sequence/17-split.html index 5d460f8ee..c9c3e3070 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/sequence/17-split.html +++ b/packages/node_modules/@node-red/nodes/locales/en-US/sequence/17-split.html @@ -14,7 +14,7 @@ limitations under the License. --> - - - -