diff --git a/Gruntfile.js b/Gruntfile.js index a6e896b9e..67739afe2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,7 +28,7 @@ module.exports = function(grunt) { var nonHeadless = grunt.option('non-headless'); if (nonHeadless) { - process.env.NODE_RED_NON_HEADLESS = 'true'; + process.env.NODE_RED_NON_HEADLESS = true; } grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), diff --git a/test/editor/editor_helper.js b/test/editor/editor_helper.js index 81e3a54f5..73177c2ca 100644 --- a/test/editor/editor_helper.js +++ b/test/editor/editor_helper.js @@ -57,6 +57,9 @@ function cleanup(flowFile) { deleteFile(homeDir + "/." + flowFile + ".backup"); deleteFile(homeDir + "/" + credentialFile); deleteFile(homeDir + "/." + credentialFile + ".backup"); + deleteFile(homeDir + "/package.json"); + deleteFile(homeDir + "/lib/flows"); + deleteFile(homeDir + "/lib"); } function deleteFile(flowFile) { @@ -66,7 +69,7 @@ function deleteFile(flowFile) { fs.unlinkSync(flowFile); } } catch (e) {} -}; +} module.exports = { startServer: function() { @@ -104,7 +107,7 @@ module.exports = { }); }); browser.url(url); - browser.waitForExist(".red-ui-palette-node[data-palette-type='inject']") + browser.waitForExist(".red-ui-palette-node[data-palette-type='inject']"); } catch (err) { console.log(err); throw err; diff --git a/test/editor/pageobjects/editor/workspace_page.js b/test/editor/pageobjects/editor/workspace_page.js index 560a0992f..f3bbf1aa2 100644 --- a/test/editor/pageobjects/editor/workspace_page.js +++ b/test/editor/pageobjects/editor/workspace_page.js @@ -15,18 +15,15 @@ **/ var when = require("when"); - var events = require("nr-test-utils").require("@node-red/runtime/lib/events.js"); - var palette = require("./palette_page"); var nodeFactory = require("../nodes/nodefactory_page"); - +var keyPage = require("../util/key_page"); var flowLayout = { flowRightEnd : 600, widthInterval : 300, heightInterval : 80 }; - var previousX = -flowLayout.widthInterval; var previousY = 0; @@ -44,6 +41,9 @@ function addNode(type, x, y) { previousY = previousY + flowLayout.heightInterval; } } + browser.waitForVisible('#red-ui-palette-search'); + browser.setValue('//*[@id="red-ui-palette-search"]/div/input', type.replace(/([A-Z])/g,' $1').toLowerCase()); + browser.pause(300); browser.waitForVisible(palette.getId(type)); browser.moveToObject(palette.getId(type)); browser.buttonDown(); @@ -57,13 +57,10 @@ function addNode(type, x, y) { } function deleteAllNodes() { - browser.waitForVisible('.red-ui-workspace-chart-event-layer'); - try { - browser.click('.red-ui-workspace-chart-event-layer'); - } catch (e) { - console.log(e); - } - browser.keys(['Control', 'a', 'a', 'Control']); // call twice to release the keys. + browser.waitForVisible('//*[contains(@class, "active")]/a[@class="red-ui-tab-label"]'); + browser.click('//*[contains(@class, "active")]/a[@class="red-ui-tab-label"]'); + browser.pause(1000); + browser.keys(keyPage.selectAll()); browser.keys(['Delete']); } @@ -78,7 +75,7 @@ function deploy() { browser.clickWithWait('#red-ui-header-button-deploy'); }); }); - browser.waitForText('#red-ui-header-button-deploy', 2000); + browser.waitForText('#red-ui-header-button-deploy', 10000); // Need additional wait until buttons becomes clickable. browser.pause(50); } diff --git a/test/editor/pageobjects/nodes/core/core/20-inject_page.js b/test/editor/pageobjects/nodes/core/common/20-inject_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/core/20-inject_page.js rename to test/editor/pageobjects/nodes/core/common/20-inject_page.js diff --git a/test/editor/pageobjects/nodes/core/core/58-debug_page.js b/test/editor/pageobjects/nodes/core/common/21-debug_page.js similarity index 86% rename from test/editor/pageobjects/nodes/core/core/58-debug_page.js rename to test/editor/pageobjects/nodes/core/common/21-debug_page.js index 52cb60f44..602d4d542 100644 --- a/test/editor/pageobjects/nodes/core/core/58-debug_page.js +++ b/test/editor/pageobjects/nodes/core/common/21-debug_page.js @@ -26,16 +26,13 @@ function debugNode(id) { util.inherits(debugNode, nodePage); -debugNode.prototype.setOutput = function(complete) { +debugNode.prototype.setOutput = function (complete) { // Open a payload type list. browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-container")]/button'); if (complete !== 'true') { // Select the "msg" type. browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options")][1]/a[1]'); // Input the path in msg. - browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-input")]/input'); - browser.keys(keyPage.selectAll()); - browser.keys(['Delete']); browser.setValue('//*[contains(@class, "red-ui-typedInput-input")]/input', complete); } else { // Select the "complete msg object" type. diff --git a/test/editor/pageobjects/nodes/core/core/80-function_page.js b/test/editor/pageobjects/nodes/core/function/10-function_page.js similarity index 89% rename from test/editor/pageobjects/nodes/core/core/80-function_page.js rename to test/editor/pageobjects/nodes/core/function/10-function_page.js index f39aba912..c1bb80a80 100644 --- a/test/editor/pageobjects/nodes/core/core/80-function_page.js +++ b/test/editor/pageobjects/nodes/core/function/10-function_page.js @@ -26,12 +26,11 @@ function functionNode(id) { util.inherits(functionNode, nodePage); -functionNode.prototype.setFunction = function(func) { +functionNode.prototype.setFunction = function (func) { browser.clickWithWait('#node-input-func-editor'); browser.keys(keyPage.selectAll()); - for (var i = 0; i < func.length; i++) { - browser.keys([func.charAt(i)]); - } + browser.keys(['Delete']); + browser.keys(func); // Delete the unnecessary code that ace editor does the autocompletion. browser.keys(keyPage.selectToEnd()); browser.keys(['Delete']); diff --git a/test/editor/pageobjects/nodes/core/logic/15-change_page.js b/test/editor/pageobjects/nodes/core/function/15-change_page.js similarity index 87% rename from test/editor/pageobjects/nodes/core/logic/15-change_page.js rename to test/editor/pageobjects/nodes/core/function/15-change_page.js index 82441c9bc..8e72afe38 100644 --- a/test/editor/pageobjects/nodes/core/logic/15-change_page.js +++ b/test/editor/pageobjects/nodes/core/function/15-change_page.js @@ -49,8 +49,8 @@ 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 ? index : 1; +changeNode.prototype.ruleSet = function (p, pt, to, tot, index) { + index = index || 1; setT("set", index); if (pt) { browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]'); @@ -68,23 +68,23 @@ changeNode.prototype.ruleSet = function(p, pt, to, tot, index) { browser.clickWithWait(totXPath); } if (to) { - browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/div/input' , to); + browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/div/input', to); } } -changeNode.prototype.ruleDelete = function(index) { - index = index ? index : 1; +changeNode.prototype.ruleDelete = function (index) { + index = index || 1; setT("delete", index); } -changeNode.prototype.ruleMove = function(p, to, index) { - index = index ? index : 1; +changeNode.prototype.ruleMove = function (p, to, 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); } -changeNode.prototype.addRule = function() { +changeNode.prototype.addRule = function () { browser.clickWithWait('//*[@id="dialog-form"]/div[3]/div/a'); } diff --git a/test/editor/pageobjects/nodes/core/logic/16-range_page.js b/test/editor/pageobjects/nodes/core/function/16-range_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/logic/16-range_page.js rename to test/editor/pageobjects/nodes/core/function/16-range_page.js diff --git a/test/editor/pageobjects/nodes/core/core/80-template_page.js b/test/editor/pageobjects/nodes/core/function/80-template_page.js similarity index 77% rename from test/editor/pageobjects/nodes/core/core/80-template_page.js rename to test/editor/pageobjects/nodes/core/function/80-template_page.js index 68cf46acf..bf1786ba2 100644 --- a/test/editor/pageobjects/nodes/core/core/80-template_page.js +++ b/test/editor/pageobjects/nodes/core/function/80-template_page.js @@ -26,21 +26,19 @@ function templateNode(id) { util.inherits(templateNode, nodePage); -templateNode.prototype.setSyntax = function(syntax) { +templateNode.prototype.setSyntax = function (syntax) { browser.selectWithWait('#node-input-syntax', syntax); } -templateNode.prototype.setFormat = function(format) { +templateNode.prototype.setFormat = function (format) { browser.selectWithWait('#node-input-format', format); } -templateNode.prototype.setTemplate = function(template) { +templateNode.prototype.setTemplate = function (template) { browser.clickWithWait('#node-input-template-editor'); browser.keys(keyPage.selectAll()); - // Need to add a character one by one since some words such as 'Control' are treated as a special word. - for (var i = 0; i < template.length; i++) { - browser.keys([template.charAt(i)]); - } + browser.keys(['Delete']); + browser.keys(template); browser.keys(keyPage.selectToEnd()); browser.keys(['Delete']); // Need to wait until ace editor correctly checks the syntax. diff --git a/test/editor/pageobjects/nodes/core/io/10-mqttconfig_page.js b/test/editor/pageobjects/nodes/core/network/10-mqttconfig_page.js similarity index 90% rename from test/editor/pageobjects/nodes/core/io/10-mqttconfig_page.js rename to test/editor/pageobjects/nodes/core/network/10-mqttconfig_page.js index 3266a1bd3..c7cdc90c5 100644 --- a/test/editor/pageobjects/nodes/core/io/10-mqttconfig_page.js +++ b/test/editor/pageobjects/nodes/core/network/10-mqttconfig_page.js @@ -23,14 +23,14 @@ function setServer(broker, port) { function clickOk() { browser.clickWithWait('#node-config-dialog-ok'); // Wait until an config dialog closes. - browser.waitForVisible('#node-config-dialog-ok', 4000, true); + browser.waitForVisible('#node-config-dialog-ok', 10000, true); } function edit() { browser.waitForVisible('#node-input-lookup-broker'); browser.click('#node-input-lookup-broker'); // Wait until a config dialog opens. - browser.waitForVisible('#node-config-dialog-ok', 4000); + browser.waitForVisible('#node-config-dialog-ok', 10000); } module.exports = { diff --git a/test/editor/pageobjects/nodes/core/io/10-mqttin_page.js b/test/editor/pageobjects/nodes/core/network/10-mqttin_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/io/10-mqttin_page.js rename to test/editor/pageobjects/nodes/core/network/10-mqttin_page.js diff --git a/test/editor/pageobjects/nodes/core/io/10-mqttout_page.js b/test/editor/pageobjects/nodes/core/network/10-mqttout_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/io/10-mqttout_page.js rename to test/editor/pageobjects/nodes/core/network/10-mqttout_page.js diff --git a/test/editor/pageobjects/nodes/core/io/21-httpin_page.js b/test/editor/pageobjects/nodes/core/network/21-httpin_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/io/21-httpin_page.js rename to test/editor/pageobjects/nodes/core/network/21-httpin_page.js diff --git a/test/editor/pageobjects/nodes/core/io/21-httprequest_page.js b/test/editor/pageobjects/nodes/core/network/21-httprequest_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/io/21-httprequest_page.js rename to test/editor/pageobjects/nodes/core/network/21-httprequest_page.js diff --git a/test/editor/pageobjects/nodes/core/io/21-httpresponse_page.js b/test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/io/21-httpresponse_page.js rename to test/editor/pageobjects/nodes/core/network/21-httpresponse_page.js diff --git a/test/editor/pageobjects/nodes/core/storage/50-filein_page.js b/test/editor/pageobjects/nodes/core/storage/10-filein_page.js similarity index 100% rename from test/editor/pageobjects/nodes/core/storage/50-filein_page.js rename to test/editor/pageobjects/nodes/core/storage/10-filein_page.js diff --git a/test/editor/pageobjects/nodes/node_page.js b/test/editor/pageobjects/nodes/node_page.js index 5234362ae..5250250e7 100644 --- a/test/editor/pageobjects/nodes/node_page.js +++ b/test/editor/pageobjects/nodes/node_page.js @@ -18,21 +18,24 @@ function Node(id) { this.id = '//*[@id="' + id + '"]'; } -Node.prototype.edit = function() { - browser.clickWithWait(this.id); - browser.clickWithWait(this.id); +Node.prototype.edit = function () { + browser.waitForVisible(this.id); + browser.moveToObject(this.id); + browser.buttonDown(); + browser.buttonUp(); + browser.keys(['Enter']); // Wait until an edit dialog opens. - browser.waitForVisible('#node-dialog-ok', 4000); + browser.waitForVisible('#node-dialog-ok', 10000); } -Node.prototype.clickOk = function() { +Node.prototype.clickOk = function () { browser.clickWithWait('#node-dialog-ok'); // Wait untile an edit dialog closes. - browser.waitForVisible('#node-dialog-ok', 4000, true); + browser.waitForVisible('#node-dialog-ok', 10000, true); browser.pause(50); } -Node.prototype.connect = function(targetNode) { +Node.prototype.connect = function (targetNode) { var outputPort = this.id + '/*[@class="red-ui-flow-port-output"]'; var inputPort = targetNode.id + '/*[@class="red-ui-flow-port-input"]'; browser.dragAndDrop(outputPort, inputPort) diff --git a/test/editor/pageobjects/nodes/nodefactory_page.js b/test/editor/pageobjects/nodes/nodefactory_page.js index c518d89ff..744c1570d 100644 --- a/test/editor/pageobjects/nodes/nodefactory_page.js +++ b/test/editor/pageobjects/nodes/nodefactory_page.js @@ -14,46 +14,46 @@ * limitations under the License. **/ -var injectNode = require('./core/core/20-inject_page'); -var debugNode = require('./core/core/58-debug_page'); -var templateNode = require('./core/core/80-template_page'); -var functionNode = require('./core/core/80-function_page'); -var mqttInNode = require('./core/io/10-mqttin_page'); -var mqttOutNode = require('./core/io/10-mqttout_page'); -var httpInNode = require('./core/io/21-httpin_page'); -var httpResponseNode = require('./core/io/21-httpresponse_page'); -var changeNode = require('./core/logic/15-change_page'); -var rangeNode = require('./core/logic/16-range_page'); -var httpRequestNode = require('./core/io/21-httprequest_page'); +var injectNode = require('./core/common/20-inject_page'); +var debugNode = require('./core/common/21-debug_page'); +var functionNode = require('./core/function/10-function_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 mqttInNode = require('./core/network/10-mqttin_page'); +var mqttOutNode = require('./core/network/10-mqttout_page'); +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 htmlNode = require('./core/parsers/70-HTML_page'); var jsonNode = require('./core/parsers/70-JSON_page'); -var fileInNode = require('./core/storage/50-filein_page'); - +var fileInNode = require('./core/storage/10-filein_page'); var nodeCatalog = { - // input + // common "inject": injectNode, - "httpIn": httpInNode, - "mqttIn":mqttInNode, - // output "debug": debugNode, - "httpResponse": httpResponseNode, - "mqttOut": mqttOutNode, // function "function": functionNode, - "template": templateNode, "change": changeNode, "range": rangeNode, + "template": templateNode, + // network + "mqttIn": mqttInNode, + "mqttOut": mqttOutNode, + "httpIn": httpInNode, + "httpResponse": httpResponseNode, "httpRequest": httpRequestNode, + // parser "html": htmlNode, - "json":jsonNode, + "json": jsonNode, // storage - "fileIn": fileInNode, -} + "fileIn": fileInNode +}; function create(type, id) { - var node = nodeCatalog[type]; - return new node(id); + var Node = nodeCatalog[type]; + return new Node(id); } module.exports = { diff --git a/test/editor/pageobjects/util/key_page.js b/test/editor/pageobjects/util/key_page.js index 9a8a867b4..509af9e22 100644 --- a/test/editor/pageobjects/util/key_page.js +++ b/test/editor/pageobjects/util/key_page.js @@ -17,21 +17,20 @@ var os = require("os"); var shortCutKeyMap = { - "selectAll": ['Control', 'a', 'Control'], + "selectAll": ['Control', 'a', 'a', 'Control'], "selectToEnd": ['Control', 'Shift', 'End', 'Shift', 'Control'], }; var shortCutKeyMapForMac = { - "selectAll": ['Command', 'a', 'Command'], + "selectAll": ['Command', 'a', 'a', 'Command'], "selectToEnd": ['Command', 'Shift', 'ArrowDown', 'Shift', 'Command'], }; function getShortCutKey(type) { - if (os.type() === "Darwin") { + if (os.type() === 'Darwin') { return shortCutKeyMapForMac[type]; - } else { - return shortCutKeyMap[type]; } + return shortCutKeyMap[type]; } function selectAll() { diff --git a/test/editor/specs/scenario/cookbook_mqtt_uispec.js b/test/editor/specs/scenario/cookbook_mqtt_uispec.js index ecfe8da7d..655074750 100644 --- a/test/editor/specs/scenario/cookbook_mqtt_uispec.js +++ b/test/editor/specs/scenario/cookbook_mqtt_uispec.js @@ -22,42 +22,45 @@ 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/io/10-mqttconfig_page.js'); +var mqttConfig = require('../../pageobjects/nodes/core/network/10-mqttconfig_page.js'); var httpNodeRoot = "/api"; var mqttServer; var mosca = require('mosca'); var moscaSettings = { - port: 1883, + port: parseInt(Math.random() * 16383 + 49152), persistence: { // Needs for retaining messages. factory: mosca.persistence.Memory } }; - // https://cookbook.nodered.org/ -describe('cookbook', function() { - beforeEach(function() { +describe('cookbook', function () { + beforeEach(function () { workspace.init(); }); - before(function() { - browser.call(function() { - return new Promise(function(resolve, reject) { - mqttServer = new mosca.Server(moscaSettings, function() { - resolve(); + before(function () { + browser.call(function () { + return new Promise(function (resolve, reject) { + mqttServer = new mosca.Server(moscaSettings, function (err) { + if (err) { + reject(err); + } else { + resolve(); + } }); }); }); helper.startServer(); }); - after(function() { - browser.call(function() { - return new Promise(function(resolve, reject) { - mqttServer.close(function() { + after(function () { + browser.call(function () { + return new Promise(function (resolve, reject) { + mqttServer.close(function () { resolve(); }); }); @@ -65,20 +68,20 @@ describe('cookbook', function() { helper.stopServer(); }); - describe('MQTT', function() { - it('Add an MQTT broker to prepare for UI test', function() { + describe('MQTT', function () { + it('Add an MQTT broker to prepare for UI test', function () { var mqttOutNode = workspace.addNode("mqttOut"); mqttOutNode.edit(); mqttConfig.edit(); - mqttConfig.setServer("localhost"); + mqttConfig.setServer("localhost", moscaSettings.port); mqttConfig.clickOk(); mqttOutNode.clickOk(); workspace.deploy(); }); - it('Connect to an MQTT broker', function() { + it('Connect to an MQTT broker', function () { var injectNode = workspace.addNode("inject"); var mqttOutNode = workspace.addNode("mqttOut"); @@ -112,7 +115,7 @@ describe('cookbook', function() { // skip this case since it is same as other cases. it.skip('Publish messages to a topic'); - it('Set the topic of a published message', function() { + it('Set the topic of a published message', function () { var injectNode = workspace.addNode("inject"); var mqttOutNode = workspace.addNode("mqttOut"); @@ -144,7 +147,7 @@ describe('cookbook', function() { debugTab.getMessage().should.eql('"22"'); }); - it('Publish a retained message to a topic', function() { + it('Publish a retained message to a topic', function () { var injectNode = workspace.addNode("inject"); var mqttOutNode = workspace.addNode("mqttOut"); @@ -182,7 +185,7 @@ describe('cookbook', function() { // skip this case since it is same as other cases. it.skip('Subscribe to a topic'); - it('Receive a parsed JSON message', function() { + it('Receive a parsed JSON message', function () { var injectNode = workspace.addNode("inject"); var mqttOutNode = workspace.addNode("mqttOut"); diff --git a/test/editor/specs/scenario/cookbook_uispec.js b/test/editor/specs/scenario/cookbook_uispec.js index 8619ddb0d..6e16ca8a7 100644 --- a/test/editor/specs/scenario/cookbook_uispec.js +++ b/test/editor/specs/scenario/cookbook_uispec.js @@ -331,7 +331,7 @@ describe('cookbook', function() { httpRequetNode.clickOk(); debugNode.edit(); - debugNode.setOutput("payload.title"); + debugNode.setOutput(".title"); debugNode.clickOk(); injectNode.connect(changeNodeSetPost); diff --git a/test/editor/wdio.conf.js b/test/editor/wdio.conf.js index 26d30570f..7bbfcbe24 100644 --- a/test/editor/wdio.conf.js +++ b/test/editor/wdio.conf.js @@ -15,7 +15,7 @@ **/ exports.config = { - + // // ================== // Specify Test Files @@ -150,12 +150,12 @@ exports.config = { // The only one supported by default is 'dot' // see also: http://webdriver.io/guide/reporters/dot.html reporters: ['spec'], - + // // Options to be passed to Mocha. // See the full list at http://mochajs.org/ mochaOpts: { - timeout: 60000, + timeout: 100000, ui: 'bdd' }, // @@ -197,7 +197,7 @@ exports.config = { */ // beforeCommand: function (commandName, args) { // }, - + /** * Hook that gets executed before the suite starts * @param {Object} suite suite details @@ -217,13 +217,13 @@ exports.config = { // beforeHook: function () { // }, /** - * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling + * Hook that gets executed _after_ a hook within the suite ends (e.g. runs after calling * afterEach in Mocha) */ // afterHook: function () { // }, /** - * Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts. + * Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) ends. * @param {Object} test test details */ // afterTest: function (test) { @@ -234,7 +234,7 @@ exports.config = { */ // afterSuite: function (suite) { // }, - + /** * Runs after a WebdriverIO command gets executed * @param {String} commandName hook command name