mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #2426 from kazuhitoyokoi/master-fixuitest
Improvements of UI testing
This commit is contained in:
commit
448de23f59
@ -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'),
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
@ -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']);
|
@ -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');
|
||||
}
|
||||
|
@ -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.
|
@ -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 = {
|
@ -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)
|
||||
|
@ -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 = {
|
||||
|
@ -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() {
|
||||
|
@ -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");
|
||||
|
||||
|
@ -331,7 +331,7 @@ describe('cookbook', function() {
|
||||
httpRequetNode.clickOk();
|
||||
|
||||
debugNode.edit();
|
||||
debugNode.setOutput("payload.title");
|
||||
debugNode.setOutput(".title");
|
||||
debugNode.clickOk();
|
||||
|
||||
injectNode.connect(changeNodeSetPost);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user