diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ea3845c2..dbd14497a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ +#### 1.0.5: Maintenance Release + +Runtime + + - #2500 Support for context stores using JSONata and evaluateNodeProperty() + - Add better handling of host-key-verify error with projects + - #2517 Handle false values in $env() properly + - #2514 Ensure complete node scope is remapped in subflows + - #2513 Flows/subflows must preinitialise their context objects + - Clear node.close timeout to avoid unnecessary work on restart + - #2532 Set flow.disabled when disabled property is false + - #2522 Ensure file context does not write 'undefined' to store + +Editor + + - #2489 Fix XPath in UI tests + - #2504 Fix paletteCategories order + - #2501 Add page objects for UI testing + - #2494 Check node props when deciding if pasted node can splice links + - #2521 Don't double-sanitize node name in debug sidebar + - #2519 German i18n updates + - #2523 Update nodeTabMap when replacing unknown nodes + - Update TypedInput to use flexbox and remove resizing code + - Handle nodes with no wires array + - Do not collapse whitespace in Debug string messages + +Nodes + + - File: Remove old legacy wording from file node info to stop confusing users. + - Join: Ensure join node handles missing buffer joiner when not in string mode + - Exec: make exec node logging consistent with itself. (only be verbose when in verbose mode) + - Trigger: reset default timeout value when switching away from wait for reset + - Join: Fix join to not crash on appending invalid types to buffer. + - MQTT out: Add warning if topic contains + or # + - #2502 WebSocket i18n update + - #2508 Add Japanese translation for join node + - TCP out: tidy up select of which rows to display + + #### 1.0.4: Maintenance Release Runtime diff --git a/packages/node_modules/@node-red/editor-client/locales/de/editor.json b/packages/node_modules/@node-red/editor-client/locales/de/editor.json index c7394c347..0c8dd42eb 100755 --- a/packages/node_modules/@node-red/editor-client/locales/de/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/de/editor.json @@ -34,11 +34,11 @@ "view" : "Ansicht", "grid" : "Gitter", "showGrid" : "Raster anzeigen", - "snapGrid" : "Einrasten am Raster", + "snapGrid" : "Am Raster ausrichten", "gridSize" : "Rastergröße", "textDir" : "Textrichtung", "defaultDir" : "Standard", - "ltr" : "Links-nach-rechts", + "ltr" : "Von links nach rechts", "rtl" : "Von rechts nach links", "auto" : "Kontextuell" }, @@ -53,15 +53,15 @@ "import" : "Import", "export" : "Exportieren", "search" : "Flows durchsuchen", - "searchInput" : "durchsuchen Sie Ihre Flows", + "searchInput" : "Flows durchsuchen", "subflows" : "Subflow", "createSubflow" : "Subflow erstellen", - "selectionToSubflow" : "Auswahl für Subflow", + "selectionToSubflow" : "Auswahl zu Subflow", "flows" : "Flows", "add" : "Hinzufügen", "rename" : "Umbenennen", "delete" : "Löschen", - "keyboardShortcuts" : "Tastaturkurzbefehle", + "keyboardShortcuts" : "Tastenkürzel", "login" : "Anmelden", "logout" : "Abmelden", "editPalette" : "Palette verwalten", @@ -217,7 +217,7 @@ "remote" : "Ferne Änderungen", "reviewChanges" : "Änderungen prüfen", "noBinaryFileShowed" : "Der Inhalt der Binärdatei kann nicht angezeigt", - "viewCommitDiff" : "Änderungen festschreiben", + "viewCommitDiff" : "Änderungen committen", "compareChanges" : "Änderungen vergleichen", "saveConflict" : "Konfliktlösung speichern", "conflictHeader" : " __resolved__ von __unresolved__ -Konflikten behoben", @@ -226,8 +226,8 @@ "newVersionError" : "Neue Version enthält keine gültige JSON-Datei:" }, "subflow" : { - "editSubflow" : "Flowschablone bearbeiten: __name__", - "edit" : "Flowsschablone bearbeiten", + "editSubflow" : "Subflow bearbeiten: __name__", + "edit" : "Subflow bearbeiten", "subflowInstances" : "Es ist __count__ Instanz dieser Subflow-Vorlage vorhanden.", "subflowInstances_plural" : "Es gibt __count__ Instanzen dieser Subflow-Vorlage.", "editSubflowProperties" : "Eigenschaften bearbeiten", @@ -266,7 +266,7 @@ } }, "keyboard" : { - "title" : "Tastaturkurzbefehle", + "title" : "Tastenkürzel", "keyboard" : "Tastatur", "filterActions" : "Filteraktionen", "shortcut" : "Direktaufruf", @@ -283,7 +283,7 @@ "exportNode" : "Node exportieren", "nudgeNode" : "Ausgewählte Nodes verschieben (1px)", "moveNode" : "Ausgewählte Nodes verschieben (20px)", - "toggleSidebar" : "Seitenleiste ein-/ausschalten", + "toggleSidebar" : "Seitenleiste ein-/ausblenden", "copyNode" : "Ausgewählte Nodes kopieren", "cutNode" : "Ausgewählte Nodes ausschneiden", "pasteNode" : "Node einfügen", @@ -308,7 +308,7 @@ }, "palette" : { "noInfo" : "Keine Informationen verfügbar", - "filter" : "Filter Nodes", + "filter" : "Nodes filtern", "search" : "Suchmodule", "addCategory" : "Neu hinzufügen ...", "label" : { @@ -366,11 +366,11 @@ "remove" : "entfernen", "update" : "Update auf __version__", "updated" : "aktualisiert", - "install" : "installieren", - "installed" : "installiert", + "install" : "Installieren", + "installed" : "Installiert", "loading" : "Kataloge werden geladen ...", "tab-nodes" : "Nodes", - "tab-install" : "installieren", + "tab-install" : "Installieren", "sort" : "Sortierung:", "sortAZ" : "a-z", "sortRecent" : "kürzlich", @@ -452,7 +452,7 @@ "name" : "Kontextdaten", "label" : "Kontext", "none" : "keine ausgewählt", - "refresh" : "Aktualisierung zum Laden", + "refresh" : "Zum Aktualisieren neu laden", "empty" : "leer", "node" : "Node", "flow" : "Flow", @@ -477,7 +477,7 @@ "none" : "Keine", "install" : "installieren", "removeFromProject" : "Aus Projekt entfernen", - "addToProject" : "zu Projekt hinzufügen", + "addToProject" : "Zu Projekt hinzufügen", "files" : "Dateien", "flow" : "Flow", "credentials" : "Berechtigungsnachweis", @@ -510,7 +510,7 @@ }, "userSettings" : { "committerDetail" : "Committer-Details", - "committerTip" : "Leer Wert für Systemstandardwert belassen", + "committerTip" : "Leer lassen für Systemstandard", "userName" : "Benutzername", "email" : "E-Mail", "sshKeys" : "SSH-Schlüssel", @@ -544,7 +544,7 @@ "revertChanges" : "Änderungen zurücksetzen", "localChanges" : "Lokale Änderungen", "none" : "Keine", - "conflictResolve" : "Alle Konflikte wurden aufgelöst. Festschreiben der Änderungen, um den Mischvorgang abzuschließen.", + "conflictResolve" : "Alle Konflikte wurden aufgelöst. Committe die Änderungen, um den Merge Request abzuschließen.", "localFiles" : "Lokale Dateien", "all" : "alle", "unmergedChanges" : "Nicht zusammengeführte Änderungen", 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/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
index 5c0778fba..8beb9f9f4 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
@@ -1109,7 +1109,7 @@ RED.nodes = (function() {
defaults: {},
label: "unknown: "+n.type,
labelStyle: "red-ui-flow-node-label-italic",
- outputs: n.outputs||n.wires.length,
+ outputs: n.outputs|| (n.wires && n.wires.length) || 0,
set: registry.getNodeSet("node-red/unknown")
}
} else {
@@ -1434,6 +1434,9 @@ RED.nodes = (function() {
delete configNodes[n.id];
} else {
nodes.splice(nodes.indexOf(n),1);
+ if (nodeTabMap[n.z]) {
+ delete nodeTabMap[n.z][n.id];
+ }
}
reimportList.push(convertNode(n));
});
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js
index cb4de12d2..88c842c2f 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js
@@ -488,56 +488,6 @@
done(labelWidth);
}
},
- _resize: function() {
- var that = this;
- if (this.uiWidth !== null) {
- this.uiSelect.width(this.uiWidth);
- }
- var type = this.typeMap[this.propertyType];
- if (type && type.hasValue === false) {
- this.selectTrigger.addClass("red-ui-typedInput-full-width");
- } else {
- this.selectTrigger.removeClass("red-ui-typedInput-full-width");
- this._getLabelWidth(this.selectTrigger, function(labelWidth) {
- that.elementDiv.css('left',labelWidth+"px");
- that.valueLabelContainer.css('left',labelWidth+"px");
- if (that.optionExpandButton.shown) {
- that.elementDiv.css('right',"22px");
- that.valueLabelContainer.css('right',"22px");
- } else {
- that.elementDiv.css('right','0');
- that.valueLabelContainer.css('right','0');
- that.input.css({
- 'border-top-right-radius': '4px',
- 'border-bottom-right-radius': '4px'
- });
- }
- if (that.optionSelectTrigger) {
- if (type && type.options && type.hasValue === true) {
- that.optionSelectLabel.css({'left':'auto'})
- that._getLabelWidth(that.optionSelectLabel, function(lw) {
- that.optionSelectTrigger.css({'width':(23+lw)+"px"});
- that.elementDiv.css('right',(23+lw)+"px");
- that.input.css({
- 'border-top-right-radius': 0,
- 'border-bottom-right-radius': 0
- });
- });
- } else {
- that.optionSelectLabel.css({'left':'0'})
- that.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'});
- if (!that.optionExpandButton.shown) {
- that.elementDiv.css({'right':0});
- that.input.css({
- 'border-top-right-radius': '4px',
- 'border-bottom-right-radius': '4px'
- });
- }
- }
- }
- });
- }
- },
_updateOptionSelectLabel: function(o) {
var opt = this.typeMap[this.propertyType];
this.optionSelectLabel.empty();
@@ -565,7 +515,6 @@
}
if (opt.hasValue) {
this.optionValue = o.value;
- this._resize();
this.input.trigger('change',this.propertyType,this.value());
}
} else {
@@ -605,11 +554,12 @@
this.propertyType = null;
this.type(currentType);
}
- setTimeout(function() {that._resize();},0);
},
width: function(desiredWidth) {
this.uiWidth = desiredWidth;
- this._resize();
+ if (this.uiWidth !== null) {
+ this.uiSelect.width(this.uiWidth);
+ }
},
value: function(value) {
var that = this;
@@ -680,8 +630,6 @@
}
else if (opt.icon.indexOf("/") !== -1) {
image = new Image();
- image.onload = function() { that._resize(); }
- image.onerror = function() { that._resize(); }
image.name = opt.icon;
image.src = mapDeprecatedIcon(opt.icon);
$('',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
@@ -693,6 +641,12 @@
if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) {
this.selectLabel.text(opt.label);
}
+ if (opt.hasValue === false) {
+ this.selectTrigger.addClass("red-ui-typedInput-full-width");
+ } else {
+ this.selectTrigger.removeClass("red-ui-typedInput-full-width");
+ }
+
if (this.optionMenu) {
this.optionMenu.remove();
this.optionMenu = null;
@@ -881,9 +835,6 @@
this._trigger("typechange",null,this.propertyType);
this.input.trigger('change',this.propertyType,this.value());
}
- if (!image) {
- this._resize();
- }
}
}
},
@@ -910,7 +861,6 @@
},
show: function() {
this.uiSelect.show();
- this._resize();
},
hide: function() {
this.uiSelect.hide();
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 = $('
.
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."
},
@@ -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.
-->
-
-
-
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..e15947fa6 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形式の文字列としてメッセージ全体を送信、受信するよう設定できます。"
},
@@ -892,7 +892,8 @@
"fixup": "最終調整式"
},
"errors": {
- "invalid-expr": "JSONata式が不正: __error__"
+ "invalid-expr": "JSONata式が不正: __error__",
+ "invalid-type": "__error__ をバッファに連結できません"
}
},
"sort": {
diff --git a/packages/node_modules/@node-red/nodes/locales/ja/sequence/17-split.html b/packages/node_modules/@node-red/nodes/locales/ja/sequence/17-split.html
index 6205bac52..dbe89d74d 100644
--- a/packages/node_modules/@node-red/nodes/locales/ja/sequence/17-split.html
+++ b/packages/node_modules/@node-red/nodes/locales/ja/sequence/17-split.html
@@ -14,7 +14,7 @@
limitations under the License.
-->
-
-
-
-
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-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/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格式的字串發送或接收整個消息物件."
},
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.
-->
-
-
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js
index 322fa2868..c395c0615 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js
@@ -420,15 +420,39 @@ function createRootContext() {
Object.defineProperties(obj, {
get: {
value: function(key, storage, callback) {
+ if (!callback && typeof storage === 'function') {
+ callback = storage;
+ storage = undefined;
+ }
+ if (callback) {
+ callback()
+ return;
+ }
return undefined;
}
},
set: {
value: function(key, value, storage, callback) {
+ if (!callback && typeof storage === 'function') {
+ callback = storage;
+ storage = undefined;
+ }
+ if (callback) {
+ callback()
+ return
+ }
}
},
keys: {
value: function(storage, callback) {
+ if (!callback && typeof storage === 'function') {
+ callback = storage;
+ storage = undefined;
+ }
+ if (callback) {
+ callback();
+ return;
+ }
return undefined;
}
}
@@ -436,25 +460,48 @@ function createRootContext() {
return obj;
}
-function getContext(localId,flowId,parent) {
- var contextId = localId;
+/**
+ * Get a flow-level context object.
+ * @param {string} flowId [description]
+ * @param {string} parentFlowId the id of the parent flow. undefined
+ * @return {object}} the context object
+ */
+function getFlowContext(flowId,parentFlowId) {
+ if (contexts.hasOwnProperty(flowId)) {
+ return contexts[flowId];
+ }
+ var parentContext = contexts[parentFlowId];
+ if (!parentContext) {
+ parentContext = createRootContext();
+ contexts[parentFlowId] = parentContext;
+ // throw new Error("Flow "+flowId+" is missing parent context "+parentFlowId);
+ }
+ var newContext = createContext(flowId,undefined,parentContext);
+ contexts[flowId] = newContext;
+ return newContext;
+
+}
+
+function getContext(nodeId, flowId) {
+ var contextId = nodeId;
if (flowId) {
- contextId = localId+":"+flowId;
+ contextId = nodeId+":"+flowId;
}
if (contexts.hasOwnProperty(contextId)) {
return contexts[contextId];
}
- var newContext = createContext(contextId,undefined,parent);
+ var newContext = createContext(contextId);
+
if (flowId) {
- var node = flows.get(flowId);
- var parent = undefined;
- if (node && node.type.startsWith("subflow:")) {
- parent = node.context().flow;
+ var flowContext = contexts[flowId];
+ if (!flowContext) {
+ // This is most likely due to a unit test for a node which doesn't
+ // initialise the flow properly.
+ // To keep things working, initialise the missing context.
+ // This *does not happen* in normal node-red operation
+ flowContext = createContext(flowId,undefined,createRootContext());
+ contexts[flowId] = flowContext;
}
- else {
- parent = createRootContext();
- }
- var flowContext = getContext(flowId,undefined,parent);
Object.defineProperty(newContext, 'flow', {
value: flowContext
});
@@ -466,6 +513,39 @@ function getContext(localId,flowId,parent) {
return newContext;
}
+//
+// function getContext(localId,flowId,parent) {
+// var contextId = localId;
+// if (flowId) {
+// contextId = localId+":"+flowId;
+// }
+// console.log("getContext",localId,flowId,"known?",contexts.hasOwnProperty(contextId));
+// if (contexts.hasOwnProperty(contextId)) {
+// return contexts[contextId];
+// }
+// var newContext = createContext(contextId,undefined,parent);
+// if (flowId) {
+// var node = flows.get(flowId);
+// console.log("flows,get",flowId,node&&node.type)
+// var parent = undefined;
+// if (node && node.type.startsWith("subflow:")) {
+// parent = node.context().flow;
+// }
+// else {
+// parent = createRootContext();
+// }
+// var flowContext = getContext(flowId,undefined,parent);
+// Object.defineProperty(newContext, 'flow', {
+// value: flowContext
+// });
+// }
+// Object.defineProperty(newContext, 'global', {
+// value: contexts['global']
+// })
+// contexts[contextId] = newContext;
+// return newContext;
+// }
+
function deleteContext(id,flowId) {
if(!hasConfiguredStore){
// only delete context if there's no configured storage.
@@ -517,6 +597,7 @@ module.exports = {
load: load,
listStores: listStores,
get: getContext,
+ getFlowContext:getFlowContext,
delete: deleteContext,
clean: clean,
close: close
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js b/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js
index cf1769700..9755681d8 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js
@@ -203,10 +203,10 @@ LocalFileSystem.prototype.open = function(){
var newContext = self.cache._export();
scopes.forEach(function(scope) {
var storagePath = getStoragePath(self.storageBaseDir,scope);
- var context = newContext[scope];
+ var context = newContext[scope] || {};
var stringifiedContext = stringify(context);
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
- log.warn(log._("error-circular",{scope:scope}));
+ log.warn(log._("context.localfilesystem.error-circular",{scope:scope}));
self.knownCircularRefs[scope] = true;
} else {
delete self.knownCircularRefs[scope];
@@ -324,7 +324,7 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
}
var stringifiedContext = stringify(obj);
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
- log.warn(log._("error-circular",{scope:scope}));
+ log.warn(log._("context.localfilesystem.error-circular",{scope:scope}));
self.knownCircularRefs[scope] = true;
} else {
delete self.knownCircularRefs[scope];
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js
index db15e8eb5..df4e810fa 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Flow.js
@@ -18,6 +18,7 @@ var clone = require("clone");
var redUtil = require("@node-red/util").util;
var flowUtil = require("./util");
var events = require("../../events");
+const context = require('../context');
var Subflow;
var Log;
@@ -54,6 +55,8 @@ class Flow {
this.catchNodes = [];
this.statusNodes = [];
this.path = this.id;
+ // Ensure a context exists for this flow
+ this.context = context.getFlowContext(this.id,this.parent.id);
}
/**
@@ -568,15 +571,18 @@ function stopNode(node,removed) {
Log.trace("Stopping node "+node.type+":"+node.id+(removed?" removed":""));
const start = Date.now();
const closePromise = node.close(removed);
+ let closeTimer = null;
const closeTimeout = new Promise((resolve,reject) => {
- setTimeout(() => {
+ closeTimer = setTimeout(() => {
reject("Close timed out");
}, nodeCloseTimeout);
});
return Promise.race([closePromise,closeTimeout]).then(() => {
+ clearTimeout(closeTimer);
var delta = Date.now() - start;
Log.trace("Stopped node "+node.type+":"+node.id+" ("+delta+"ms)" );
}).catch(err => {
+ clearTimeout(closeTimer);
node.error(Log._("nodes.flows.stopping-error",{message:err}));
Log.debug(err.stack);
})
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js
index a7bec1234..08d8b21d7 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js
@@ -16,7 +16,7 @@
const clone = require("clone");
const Flow = require('./Flow').Flow;
-
+const context = require('../context');
const util = require("util");
const redUtil = require("@node-red/util").util;
@@ -154,7 +154,7 @@ class Subflow extends Flow {
var ui = old ? old.ui : null;
env[e.name] = e;
if (ui) {
- env[e.name].ui = ui;
+ env[e.name].ui = ui;
}
});
}
@@ -227,6 +227,10 @@ class Subflow extends Flow {
this.node.on("input", function(msg) { this.send(msg);});
this.node.on("close", function() { this.status({}); })
this.node.status = status => this.parent.handleStatus(this.node,status);
+ // Create a context instance
+ // console.log("Node.context",this.type,"id:",this._alias||this.id,"z:",this.z)
+ this._context = context.get(this._alias||this.id,this.z);
+
this.node._updateWires = this.node.updateWires;
@@ -466,7 +470,7 @@ function createNodeInSubflow(subflowInstanceId, def) {
* properties in the nodes object to reference the new node ids.
* This handles:
* - node.wires,
- * - node.scope of Catch and Status nodes,
+ * - node.scope of Complete, Catch and Status nodes,
* - node.XYZ for any property where XYZ is recognised as an old property
* @param {[type]} nodes [description]
* @param {[type]} nodeMap [description]
@@ -489,7 +493,7 @@ function remapSubflowNodes(nodes,nodeMap) {
}
}
}
- if ((node.type === 'catch' || node.type === 'status') && node.scope) {
+ if ((node.type === 'complete' || node.type === 'catch' || node.type === 'status') && node.scope) {
node.scope = node.scope.map(function(id) {
return nodeMap[id]?nodeMap[id].id:""
})
diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js
index 16166fbc1..1a350ce7c 100644
--- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/index.js
@@ -553,7 +553,7 @@ function getFlow(id) {
if (flow.label) {
result.label = flow.label;
}
- if (flow.disabled) {
+ if (flow.hasOwnProperty('disabled')) {
result.disabled = flow.disabled;
}
if (flow.hasOwnProperty('info')) {
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)) {
diff --git a/packages/node_modules/@node-red/util/lib/util.js b/packages/node_modules/@node-red/util/lib/util.js
index 45a33be14..3e35f7fde 100644
--- a/packages/node_modules/@node-red/util/lib/util.js
+++ b/packages/node_modules/@node-red/util/lib/util.js
@@ -558,15 +558,19 @@ 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);
- return (val ? val : "");
+ if (typeof val !== 'undefined') {
+ return val;
+ } else {
+ return ""
+ }
})
expr.registerFunction('clone', cloneMessage, '<(oa)-:o>');
expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
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: {
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 59ac4f2f1..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,41 +51,82 @@ 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 () {
- 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/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/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;
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();
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
index 3c9030b03..6fd76a421 100644
--- a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js
+++ b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js
@@ -32,17 +32,20 @@ describe('context', function() {
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");
@@ -55,12 +58,14 @@ describe('context', function() {
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");
@@ -68,6 +73,7 @@ describe('context', function() {
});
it('keeps local context local', function() {
+ var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
var context2 = Context.get("2","flowA");
@@ -79,6 +85,7 @@ describe('context', function() {
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");
@@ -91,6 +98,8 @@ describe('context', function() {
});
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");
@@ -103,6 +112,9 @@ describe('context', function() {
});
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");
@@ -115,6 +127,7 @@ describe('context', function() {
});
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);
@@ -122,6 +135,7 @@ describe('context', function() {
})
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);
@@ -130,6 +144,7 @@ describe('context', function() {
})
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");
@@ -142,6 +157,7 @@ describe('context', function() {
});
it('enumerates context keys - sync', function() {
+ var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
var keys = context.keys();
@@ -160,6 +176,7 @@ describe('context', function() {
});
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) {
@@ -183,6 +200,7 @@ describe('context', function() {
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();
@@ -195,6 +213,7 @@ describe('context', function() {
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) {
@@ -210,6 +229,7 @@ describe('context', function() {
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');
@@ -219,6 +239,7 @@ describe('context', function() {
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);
@@ -227,40 +248,67 @@ describe('context', function() {
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", context0);
+ 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", context0);
- var parent = context1.get("$parent.$parent.K");
+ var context1 = Context.get("1","flowB");
+ var parent = context1.flow.get("$parent.$parent.K");
should.equal(parent, undefined);
});
- it('should get value in $parent', function() {
+ 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", context0);
- context0.set("K", "v");
- var v = context1.get("$parent.K");
+ 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", context0);
- context1.set("$parent.K", "v");
- var v = context0.get("K");
+ 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", context0);
+ var context1 = Context.get("1","flowB");
var parent = context1.get("$parent");
- context0.set("K0", "v0");
+ flowContextA.set("K0", "v0");
context1.set("K1", "v1");
var keys = context1.keys();
keys.should.have.length(1);
@@ -366,6 +414,7 @@ describe('context', function() {
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);
@@ -452,6 +501,7 @@ describe('context', function() {
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);
@@ -465,6 +515,7 @@ describe('context', function() {
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);
@@ -479,6 +530,7 @@ describe('context', function() {
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);
@@ -493,6 +545,7 @@ describe('context', function() {
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);
@@ -510,6 +563,7 @@ describe('context', function() {
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);
@@ -527,6 +581,7 @@ describe('context', function() {
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);
@@ -541,10 +596,11 @@ describe('context', function() {
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");
@@ -561,6 +617,7 @@ describe('context', function() {
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);
@@ -575,6 +632,7 @@ describe('context', function() {
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);
@@ -584,6 +642,7 @@ describe('context', function() {
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);
@@ -595,6 +654,7 @@ describe('context', function() {
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");
@@ -615,6 +675,7 @@ describe('context', function() {
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) {
@@ -647,6 +708,7 @@ describe('context', function() {
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");
@@ -667,6 +729,7 @@ describe('context', function() {
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) {
@@ -685,6 +748,7 @@ describe('context', function() {
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") {
@@ -702,6 +766,7 @@ describe('context', function() {
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") {
@@ -716,6 +781,7 @@ describe('context', function() {
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) {
@@ -739,6 +805,7 @@ describe('context', function() {
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) {
@@ -777,6 +844,7 @@ describe('context', function() {
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) {
@@ -804,6 +872,7 @@ describe('context', function() {
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) {
@@ -831,6 +900,7 @@ describe('context', function() {
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) {
@@ -859,6 +929,7 @@ describe('context', function() {
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") {
@@ -873,6 +944,7 @@ describe('context', function() {
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.");
@@ -884,6 +956,7 @@ describe('context', function() {
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();
@@ -893,6 +966,7 @@ describe('context', function() {
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.");
@@ -904,6 +978,7 @@ describe('context', function() {
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();
@@ -913,6 +988,7 @@ describe('context', function() {
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.");
@@ -924,6 +1000,7 @@ describe('context', function() {
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();
@@ -953,7 +1030,6 @@ describe('context', function() {
}).catch(done);
});
});
-
describe('delete context',function(){
it('should not call delete() when external context storage is used', function(done) {
Context.init({contextStorage:contextDefaultStorage});