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 = $('

'+ + 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/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 7a561d9cd..80a9432f7 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -3531,8 +3531,10 @@ RED.view = (function() { if (new_ms.length === 1) { node = new_ms[0]; spliceActive = node.n.hasOwnProperty("_def") && - node.n._def.inputs > 0 && - node.n._def.outputs > 0; + ((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) && + ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) + + } } RED.keyboard.add("*","escape",function(){ diff --git a/packages/node_modules/@node-red/editor-client/src/sass/debug.scss b/packages/node_modules/@node-red/editor-client/src/sass/debug.scss index c92c43320..1e77e46e1 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/debug.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/debug.scss @@ -217,6 +217,10 @@ .red-ui-debug-msg-type-number { color: $debug-message-text-color-msg-type-number; }; .red-ui-debug-msg-type-number-toggle { cursor: pointer;} +.red-ui-debug-msg-type-string { + white-space: pre-wrap; +} + .red-ui-debug-msg-row { display: block; padding: 4px 2px 2px; diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss index be2d50674..243c42e17 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/typedInput.scss @@ -18,7 +18,7 @@ border: 1px solid $form-input-border-color; border-radius: 4px; height: 34px; - display: inline-block; + display: inline-flex; padding: 0; margin: 0; vertical-align: middle; @@ -26,12 +26,7 @@ overflow:visible; position: relative; .red-ui-typedInput-input-wrap { - position: absolute; - left:0; - right:0; - top:0; - bottom:0; - outline: red; + flex-grow: 1; } input.red-ui-typedInput-input { width: 100%; @@ -49,7 +44,7 @@ border-color: $form-input-focus-color !important; } .red-ui-typedInput-value-label { - position: absolute; + flex-grow: 1; display: inline-block; height: 32px; box-sizing: border-box; @@ -104,12 +99,11 @@ button.red-ui-typedInput-option-trigger { text-align: left; border: none; - position: absolute; + flex-basis: auto; box-sizing: border-box; border-top-left-radius: 4px; border-bottom-left-radius: 4px; padding: 0 1px 0 5px; - display:inline-block; background: $form-button-background; height: 32px; line-height: 30px; @@ -151,7 +145,7 @@ button.red-ui-typedInput-option-trigger text-decoration: none; } &.red-ui-typedInput-full-width { - width: 100%; + flex-grow: 1; border-top-right-radius: 4px; border-bottom-right-radius: 4px; } @@ -167,7 +161,6 @@ button.red-ui-typedInput-option-expand { border-bottom-right-radius: 4px; border-top-left-radius: 0; border-bottom-left-radius: 0; - right: 0; } button.red-ui-typedInput-option-trigger { @@ -176,8 +169,8 @@ button.red-ui-typedInput-option-trigger { border-top-right-radius: 4px; border-bottom-right-radius: 4px; padding: 0 0 0 0; - position:absolute; - right: 0; + position:relative; + flex-grow: 1; .red-ui-typedInput-option-label { background:$form-button-background; color: $form-text-color; diff --git a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js index ddab5bbe2..64068ea86 100644 --- a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js +++ b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js @@ -457,7 +457,7 @@ RED.debug = (function() { var metaRow = $('
').appendTo(msg); $(''+ getTimestamp()+'').appendTo(metaRow); if (sourceNode) { - $('',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+sanitize(o.name||sourceNode.name||sourceNode.id)) + $('',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+(o.name||sourceNode.name||sourceNode.id)) .appendTo(metaRow) .on("click", function(evt) { evt.preventDefault(); 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. --> - 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); diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js index 3f5b2dff4..7ab624222 100644 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js +++ b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js @@ -469,6 +469,7 @@ module.exports = function(RED) { this.broker = n.broker; this.brokerConn = RED.nodes.getNode(this.broker); var node = this; + var chk = /[\+#]/; if (this.brokerConn) { this.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"}); @@ -487,6 +488,7 @@ module.exports = function(RED) { } if ( msg.hasOwnProperty("payload")) { if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist + if (chk.test(msg.topic)) { node.warn(RED._("mqtt.errors.invalid-topic")); } this.brokerConn.publish(msg, done); // send the message } else { node.warn(RED._("mqtt.errors.invalid-topic")); 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/core/network/31-tcpin.html b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html index 6029d61ce..61b840268 100644 --- a/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html +++ b/packages/node_modules/@node-red/nodes/core/network/31-tcpin.html @@ -170,15 +170,14 @@ $("#node-input-port-row").hide(); $("#node-input-host-row").hide(); $("#node-input-end-row").hide(); + } else if (sockettype == "client"){ + $("#node-input-port-row").show(); + $("#node-input-host-row").show(); + $("#node-input-end-row").show(); } else { $("#node-input-port-row").show(); - $("#node-input-end-row").show(); - } - - if (sockettype == "client") { - $("#node-input-host-row").show(); - } else { $("#node-input-host-row").hide(); + $("#node-input-end-row").show(); } }; updateOptions(); 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/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 @@ - - - - - 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..d668ee922 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." }, @@ -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});