1
0
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:
Nick O'Leary 2020-01-17 10:18:05 +00:00 committed by GitHub
commit 448de23f59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 105 additions and 106 deletions

View File

@ -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'),

View File

@ -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;

View File

@ -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);
}

View File

@ -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.

View File

@ -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']);

View File

@ -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');
}

View File

@ -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.

View File

@ -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 = {

View File

@ -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)

View File

@ -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 = {

View File

@ -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() {

View File

@ -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() {
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");

View File

@ -331,7 +331,7 @@ describe('cookbook', function() {
httpRequetNode.clickOk();
debugNode.edit();
debugNode.setOutput("payload.title");
debugNode.setOutput(".title");
debugNode.clickOk();
injectNode.connect(changeNodeSetPost);

View File

@ -155,7 +155,7 @@ exports.config = {
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
mochaOpts: {
timeout: 60000,
timeout: 100000,
ui: 'bdd'
},
//
@ -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) {