Element could not be located error in nightwatch js - javascript

I want to confirm that each my string have text 'Operational'. Here is my code
module.exports = {
test: function (client) {
var text1;
client
.maximizeWindow()
.url('https://status.gitlab.com/')
.waitForElementVisible('img[alt="Logo"]', 10 * 1000)
.elements('css selector', '.component',
function (elements) {
elements.value.forEach(function (elementsObj) {
client.elementIdText(elementsObj.ELEMENT, function (result) {
text1 = result.value
text1 = text1.replace(/\s+/g, '')
console.log(text1);
client.assert.containsText(text1, 'Operational')
})
})
})
}
};
When I run this I receive error - Testing if element <WebsiteGoogleComputeEngineOperational> contains text 'Operational' in 5000ms - expected "contains text 'Operational'" but got: "element could not be located" (5341ms)
When I run without client.assert.containsText(text1, 'Operational') I receive full list of my strings
WebsiteGoogleComputeEngineOperational
APIGoogleComputeEngineOperational
Git(sshandhttps)GoogleComputeEngineOperational
PagesGoogleComputeEngineOperational
CI/CDGoogleComputeEngineOperational
BackgroundProcessingGoogleComputeEngineOperational
SupportServicesZendeskOperational
packages.gitlab.comAWSOperational
customers.gitlab.comAzureOperational
version.gitlab.comAWSOperational
forum.gitlab.comDigitalOceanOperational
WindowsRunners(beta)GoogleComputeEngineOperational
CanaryGoogleComputeEngineOperational
dashboards.gitlab.comGoogleComputeEngineOperational
Where is the problem and how I can resolve this?

You cannot use client.assert.containsText(text1, 'Operational') as this is meant for validating the innertext of the element. What you're doing is comparing two texts for which you can use a simple if- else.
module.exports = {
test: function(client) {
var text1;
client
.maximizeWindow()
.url('https://status.gitlab.com/')
.waitForElementVisible('img[alt="Logo"]', 10 * 1000)
.elements('css selector', '.component',
function(elements) {
elements.value.forEach(function(elementsObj) {
client.elementIdText(elementsObj.ELEMENT, function(result) {
text1 = result.value.replace(/\s+/g, '')
if (text1.includes('Operational')) {
console.log('Found Operational in - ' + '"' + text1 + '"' + '\n')
} else {
console.log('Didn\'t Found Operational in - ' + '"' + text1 + '"' + '\n')
}
})
})
})
}
};
Upon Execution, you can see something like this in console -

Related

Nightwatch count number of element

I'am a newbie with Nightwatch and i would like to count the number of element. I am able to do this but I don't understand how the variable is interpreted, here my code :
browser.elements('xpath', '/html/body/div[1]/div[3]/div[2]/div/div[1]/div/div[3]/table/tbody/tr/td[2]', function (elements) {
var nb = 0
elements.value.forEach(function (elementsObj, index) {
browser.elementIdText(elementsObj.ELEMENT, function (result) {
if (result.value != "") {
nb = nb + 1
console.log(result.value)
}
//console.log("There are : " + nb)
})
//console.log("There are : " + nb)
})
This display all elements I want. The output is :
Element 1
Element 2
and so on...
Now, I would like to have this :
There are X elements :
Element 1
Element 2
And so on...
But I try to print my variable "nb" but it doesn't work... How can I store and display my "nb" variable ?
Thanks,
My guess is that those commented console.log when not commented are executed before your loop ends... and therefore they are most likely returning :
"There are : 0"
Have you tried to "await" for the forEach loop to end?
Maybe something like:
browser.elements('xpath', '/html/body/div[1]/div[3]/div[2]/div/div[1]/div/div[3]/table/tbody/tr/td[2]', function (elements) {
var nb = 0
elements.value.forEach(async function (elementsObj, index) {
await browser.elementIdText(elementsObj.ELEMENT, function (result) {
if (result.value != "") {
nb = nb + 1
console.log(result.value)
}
//console.log("There are : " + nb)
})
console.log("There are : " + nb)
})
You can store the values in an array. And then parse the length after the forEach loop is completed. something like this:
browser.elements('xpath', '/html/body/div[1]/div[3]/div[2]/div/div[1]/div/div[3]/table/tbody/tr/td[2]', function (elements) {
var nb = []
elements.value.forEach(function (elementsObj, index) {
browser.elementIdText(elementsObj.ELEMENT, function (result) {
if (result.value != "") {
console.log(result.value)
nb.push(result.value)
}
})
})
console.log('There are ' + nb.length() + 'elements')
for (const nbItem of nb) {
console.log(nbItem)
}
})

Make a change to Javascript code that renumbers a list

I am unfamiliar with Javascript, but I would like to make a change to the following code. Currently, it renames macros in the program Keyboard Maestro in the format of 01), 02), etc. I would like it to simply use the format of a number followed by a space instead (i.e. 1 , 2 , etc.) I hope what I described makes sense. Thanks!
Part 1:
(function(inDesignMode) {
'use strict';
function dump(obj, desc) {
console.log((desc ? desc + ": " : "") + JSON.stringify(obj, null, "\t"));
}
var KMEditor = (function() {
var _editorAppName = "Keyboard Maestro";
var _editorApp;
return {
getEditorApp: function() {
return _editorApp ? _editorApp : _editorApp = Application(_editorAppName);
},
getEditorAppName: function() {return _editorAppName;},
getSelectedMacrosOrGroups: function() {
return this.getEditorApp().selectedmacros();
}
};
})();
var KMEngine = (function() {
var _engineApp;
return {
getAllMacrosSourceFileName: function() {
return this.getAppSupportFolderName() + "Keyboard Maestro Macros.plist";
},
getAppSupportFolderName: function() {
var app = Application.currentApplication();
app.includeStandardAdditions = true;
return app.pathTo('application support', { from: 'user domain' }) +
"/Keyboard Maestro/";
},
getEngineAppName: function() {
return "Keyboard Maestro Engine";
},
getEngineApp: function() {
if (!_engineApp)
_engineApp = Application(this.getEngineAppName());
return _engineApp;
},
readPlistBinaryFile: function(path) {
var data = $.NSData.dataWithContentsOfFile(path);
return ObjC.deepUnwrap(
$.NSPropertyListSerialization.propertyListWithDataOptionsFormatError(
data, $.NSPropertyListBinaryFormat_v1_0, 0, null));
},
};
})();
function getMacrosInfo(selectedMacroUUIDs) {
function getMacroName(macro) {
if (macro.Name)
return macro.Name;
throw Error("Un-named Macro UUID: " + macro.UID);
}
function getMacroInfo(macro) {
var info = {
macroUUID: macro.UID,
macroName: getMacroName(macro)
};
var matches = info.macroName.match(/^(\d\d)?(\))?(.*)/);
if (!matches || matches.length != 4)
throw Error("Could not parse macro name '" + info.macroName + "'");
info.prefixNumber = matches[1] || "";
info.macroNameNoPrefix = matches[3].trim();
return info;
}
function reOrderResult(macrosInfo, selectedMacroUUIDs) {
macrosInfo.macros = selectedMacroUUIDs.map(function(selectedMacroUUID) {
var info = macrosInfo.macros.find(function(macroInfo) {
return macroInfo.macroUUID === selectedMacroUUID;
});
if (!info)
throw Error("Could not find macro information for UUID '" + selectedMacroUUID + "'");
return info;
});
}
// getMacrosInfo()
var result = {
macros: []
};
var macroGroup;
var plist = KMEngine.readPlistBinaryFile(KMEngine.getAllMacrosSourceFileName());
plist.MacroGroups.forEach(function(group) {
if (group.Macros) {
group.Macros.forEach(function(macro) {
if (selectedMacroUUIDs.indexOf(macro.UID) >= 0) {
if (!result.groupUUID) {
macroGroup = group;
result.groupUUID = group.UID;
result.groupName = group.Name;
result.groupToggleMacroUID = group.ToggleMacroUID;
} else if (result.groupUUID !== group.UID) {
throw Error("Selected macros must all be from the same group");
}
result.macros.push(getMacroInfo(macro));
}
});
}
});
reOrderResult(result, selectedMacroUUIDs);
return result;
} // getMacrosInfo()
function execute() {
var selectedMacroUUIDs = KMEditor.getSelectedMacrosOrGroups();
if (!selectedMacroUUIDs || selectedMacroUUIDs.length < 2)
throw Error("You must select two or more macros");
var result = getMacrosInfo(selectedMacroUUIDs);
return JSON.stringify(result);
}
if (inDesignMode) {
return execute();
} else {
try {
return execute();
} catch (e) {
return "Error: " + e.message;
}
}
})(false);
Part 2:
(function(inDesignMode, designModeParams) {
'use strict';
var _kme = Application("Keyboard Maestro Engine");
function getKMVariable(name, required) {
var result = undefined;
if (inDesignMode && designModeParams)
result = designModeParams[name];
if (result === undefined)
result = _kme.getvariable(name);
if (required && !result)
throw Error("Variable '" + name + "' is empty");
return result;
}
var _km = Application("Keyboard Maestro");
function execute() {
var macroId = getKMVariable("palorg_MacroUUID", true);
var newName = getKMVariable("palorg_NewMacroName", true);
var macros = _km.macros.whose({id: {"=": macroId}});
if (macros.length == 0)
throw new Error("Macro '" + macroId + "' not found");
var macro = macros[0];
macro.name = newName;
return "OK";
}
if (inDesignMode) {
try {
return execute();
} catch (e) {
return "Error on line: " + e.line + ": " + e.message;
} finally {
try {
Application("Atom").activate();
} catch (e) {
}
}
} else {
try {
return execute();
} catch (e) {
return "Error: " + e.message;
}
}
})(false);
As per clarifications obtained in your comment, this is what you need:
01)macro --> 1 macro
To get that, you can try adding the following lines in execute() function in Part 2:
const regExp = /^0(.)\)/i; //Creates the regular expression
newName.replace(regex, '$1 '); //Replaces the match from the regex with a substring
macro.name = newName;
This is a regular expression substring replacement (more details here). Here's the breakdown on what's being done.
The ^ symbol denotes that a substring is being searched from the beginning of the string.
The next 0 is just a literal search for a 0 at the beginning of the string.
The next ( starts a parenthesized submatch string whose utility will be explained later.
The . indicates any single character.
The next ) end the parenthesized submatch string started earlier.
The next \) is for searching a ) literally which needs to be escaped with \ since ) has a special meaning of ending a parenthesized submatch string as mentioned above.
Now, the parenthesized submatch string is used so that after matching against the regular expression, the result can be used further to replace the matched string with. In this case,
01)macro will match 1 as the parenthesized submatch string since that's a single character after the leading 0 and before the ). This is used as $1 in the replace function so that after matching 01), it's replaced with 1 (notice the extra space, as is required by the question).

Why String.Prototype replace doesn't work inside nested functions?

I have declared in same script file the following sub-string replace function :
String.prototype.replaceAt = function(index, character) {
index = parseInt(index, 10);
return this.substr(0, index) + character + this.substr(index + character.length);
}
If I use this function in the main script file (for example right after its declaration), it works properly with string output.
If I use this function inside nested functions, more exactly I have a function inside another function and I call "replaceAt" inside the second function, it doesn't work and it truncates all characters after the "index" specified in "replaceAt". I also specify that this is a content script in a Chrome extension.
Example (works okay outside functions, in main file) :
var h = '000000';
h = h.replaceAt(3, "1");
console.log(h);
Example (truncates everything after "index") :
function do_lut() {
temp = '000000000000000';
function nfu_change(e, i) {
if (e.checked) {
if (temp != null) {
console.log(i + " - " + temp);
temp = temp.replaceAt(i, "1");
} else { temp = '000000000000000'.replaceAt(i, "1"); }
}
else { if(temp!=null) {temp = temp.replaceAt(i,"0");} else {temp = new String('000000000000000');} }
console.log(i+" - "+temp);
}
}
for(i=0;i<15;i++)
{
nfu[i] = document.createElement('INPUT');
nfu[i].type = 'checkbox';
nfu[i].id = Math.floor((Math.random() * 100000000) + 1).toString();
nfu[i].name = i.toString();
nfu[i].onchange = function(e) {nfu_change(e.target,e.target.name);}
}
}
The above will create a list of checkbox inputs and when user checks/unchecks a box, the respective index (corresponding to the input in the list) will be changed to either "0" or "1" true/false in "temp" which will be overwritten in a cookie. So, "nfu_change" is called conditionally on change of checkbox status.
My hypothesis is that this doesn't mean what you think it means. Run your code thru a debugger and see what value this takes on in the places where your function does and does not do what you expect.
Edit I think that the above hypothesis is wrong.
I was able to reproduce your problem by calling your replaceAt function with a string for the index.
String.prototype.replaceAt = function (index, character) {
return this.substr(0, index) + character + this.substr(index + character.length);
}
alert("abc".replaceAt(1, "B")); // aBc
alert("abc".replaceAt("1", "B")); //aB
Here's the solution:
String.prototype.replaceAt = function (index, character) {
index = parseInt(index, 10);
return this.substr(0, index) + character + this.substr(index + character.length);
}
alert("abc".replaceAt(1, "B")); // aBc
alert("abc".replaceAt("1", "B")); //aBc

Replacing placeholder values within a string

Not sure why my code is not working because the code I took from below is working in JSFiddle here
String.prototype.interpolate = (function() {
var re = /\[(.+?)\]/g;
return function(o) {
return this.replace(re, function(_, k) {
return o[k];
});
}
}());
var _obj = {
hey: 'Hey',
what: 'world',
when: 'today'
}
document.write(
'[hey]-hey, I saved the [what] [when]!'.interpolate(_obj)
);
But my code below doesn't seem to be doing the same thing. It still has the bracket values [NAME][ADDRESS][PHONE] and not the replaced data values:
$(document).ready(function () {
$('#goSearching').click(function () {
var isNumber = $.isNumeric($('#searchBox').val());
var theSearch = $('#searchBox').val();
var theURL = '';
if (isNumber) {
if (theSearch.length >= 10) {
//Search by NPI#
$('#titleOfSearchResult').html('P search results by NPI #:');
theURL = 'http://zzzzz.com:151/P/GetNPI/';
} else {
//Search by P #
$('#titleOfSearchResult').html('P search results by P #:');
theURL = 'http://zzzzzz.com:151/P/GetNo/';
}
} else {
//Search by P Name
$('#titleOfSearchResult').html('P search results by P Name:');
theURL = 'http://zzzzz.com:151/P/PName/';
}
$.ajax({
url : theURL + $('#searchBox').val() + '/',
type : 'GET',
dataType : 'xml',
timeout : 10000,
cache : false,
crossDomain : true,
success : function (xmlResults) {
console.log(xmlResults);
var _htmlObj = {};
$(xmlResults).find("P").each(function() {
_htmlObj = {
NAME : $(this).find("pName").text(),
ADDRESS : $(this).find("pSpecialty").text(),
PHONE : $(this).find("pUserid").text()
}
console.log($(this).find("pName").text());
console.log($(this).find("pSpecialty").text());
console.log($(this).find("pUserid").text());
$("#theResults").append(
'<p><strong>[NAME]</strong></p>' +
'<p>[ADDRESS]</p>' +
'<p>[PHONE]</p>' +
'<p> </p>'.interpolate(_htmlObj)
);
});
},
error : function(jqXHR, textStatus) {
console.log("error: " + textStatus);
}
});
});
String.prototype.interpolate = (function () {
var re = /\[(.+?)\]/g;
return function (o) {
return this.replace(re, function (_, k) {
return o[k];
});
}
}());
});
In the console it outputs the correct returned values but it won't replace those values with the placeholders [NAME][ADDRESS][PHONE].
Any help to fix this issue would be great! Thanks!
You're only calling interpolate on the last string
'<p><strong>[NAME]</strong></p>' +
'<p>[ADDRESS]</p>' +
'<p>[PHONE]</p>' +
'<p> </p>'.interpolate(_htmlObj)
Member access has a higher precedence than addition, so it needs to be:
('<p><strong>[NAME]</strong></p>' +
'<p>[ADDRESS]</p>' +
'<p>[PHONE]</p>' +
'<p> </p>').interpolate(_htmlObj)
Example from #TrueBlueAussie: jsfiddle.net/TrueBlueAussie/fFSYA/9

Using yepnope, how can I know if a css resource was loaded successfully?

I'm developping a webapp for internal use in a company with a strict (and evolving) firewall.
I'm using yepnope.js / Modernizr.load to load js and css files from CDN with fallbacks.
But how do I know if a css was loaded successfully ? Is there a way to test successful loading and activation of a css ?
Thanks to the hint from #Morpheus, here is a solution :
First, in order to not depend on anything (jQuery, etc.) we need to define 2 functions :
One to get the css property of an element :
from http://robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
function getStyle(oElm, strCssRule) {
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle){
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle){
strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
},
Another to create an hidden DOM element and test its css properties :
from https://stackoverflow.com/a/8122005/587407
function test_css(element_name, element_classes, expected_styles, rsrc_name, callback) {
// create an element of given type
var elem = document.createElement(element_name);
// immediately hide it
elem.style.display = 'none';
// give it the class(es)
for (var i = 0; i < element_classes.length; i++) {
elem.className = elem.className + " " + element_classes[i];
}
// and add it to the DOM
document.body.appendChild(elem);
// test the given properties
// it happens (~1/3 times in local)
// that the css takes a few ms to apply.
// so we need a function that we can try directly first
// and retry again later in case of failure
var handle = -1;
var try_count = 0;
var test_function = function() {
var match = true; // so far
try_count++;
for (var key in expected_styles) {
console.log("[CSS loader] testing " + rsrc_name + " css : " + key + " = '" + expected_styles[key] + "', actual = '" + get_style_of_element(elem, key) + "'");
if(get_style_of_element(elem, key) === expected_styles[key]) {
match = match && true;
} else {
console.error("[CSS loader] css " + rsrc_name + " test failed (not ready yet ?) : " + key + " = " + expected_styles[key] + ", actual = " + get_style_of_element(elem, key) );
match = false;
}
}
if (match === true || try_count >= 3) {
if (handle >= 0)
window.clearTimeout(handle);
// remove our test element from the DOM
document.body.removeChild(elem);
if (!match)
console.error("[CSS loader] giving up on css " + rsrc_name + "..." );
else
console.log("[CSS loader] css " + rsrc_name + " load success !" );
callback(rsrc_name, match);
}
return match;
}
// use and program the function
if(! test_function() ) {
console.info("" + rsrc_name + " css test failed, programming a retry...");
handle = window.setInterval(test_function, 100);
}
}
Note that the previous function is tricky because of the delayed loading of css.
We can now use yepnope like this :
{
load: { 'bootstrap-css': 'http//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css' },
callback: function (url, result, key) {
// here for bootstrap, I test presence of .span1 class
test_css('span', [ 'span1' ], { 'width': '60px' }, function(match) {
if( match ) {
// it worked !
}
else {
// ... fallback on another CDN or local rsrc...
}
}
}

Categories

Resources