I am new to using nightwatch.js.
I want to get a list of elements and verify text value of each and every element with a given string.
I have tried :
function iter(elems) {
elems.value.forEach(function(element) {
client.elementIdValue(element.ELEMENT)
})
};
client.elements('css selector', 'button.my-button.to-iterate', iter);
For another stackoverflow question
But what I am using right now is
waitForElementPresent('elementcss', 5000).assert.containsText('elementcss','Hello')
and it is returning me the output
Warn: WaitForElement found 5 elements for selector "elementcss". Only the first one will be checked.
So I want that it should verify text value of each and every element of list.
All the things can not be done by nightwatch js simple commands , so they have provided the custom command means selenium protocol. Here you can have all the selenium protocol. I have used following code to assert text value of each and every element with a given string "text". Hope it will help you
module.exports = {
'1. test if multiple elements have the same text' : function (browser) {
function iter(elems) {
elems.value.forEach(function(element) {
browser.elementIdText(element.ELEMENT, function(result){
browser.assert.equal(result.value,'text')
})
})
};
browser
.url('file:///home/user/test.html')
.elements('tag name', 'a', iter);
}
};
My HTML snippet
<div id="test">
<a href="google.com" class='red'> text </a>
<a href="#" class='red'> text </a>
<a href="#" class='red'> text 1</a>
</div>
I was able to do it as :
.elements('css selector', 'cssValue', function (elements) {
for(var i=0;i<elements.value.length;i++){
var elementCss = 'div.search-results-item:nth-child(' + (i+1) + ') span';
client.assert.containsText(elementCss,'textValue');
}
})
Put your function iter in a for loop and before that use
client.elements('css selector', '#CollectionClass', function (result) {
if(result.value.length > 1) {
var count;
for(count=1; count<result.value.length; count++) {
result.value.forEach(function(element) {
client.elementIdValue(element.ELEMENT);
client.elementIdText(selectedHighlight.ELEMENT, function(resuddlt) {
this.assert.equal(typeof resuddlt, "object");
this.assert.equal(resuddlt.status, 0);
this.assert.equal(resuddlt.value, "your value");
});
}
}
}
};
You have stated what you have tried (which is good) but you haven't presented us with sanitized HTML that demonstrates the problem (which reduces precision in possible answers).
There are many ways in HTML to contain information, and the built-in Nightwatch containsText will serialize any text it finds within a structure that contains substructures.
So for example, if you have as Juhi suggested,
<div id="test">
<a href="google.com" class='red'> text </a>
<a href="#" class='red'> text </a>
<a href="#" class='red'> text 1</a>
</div>
Then the assertions
.verify.containsText('#test', ' text ') // first one
.verify.containsText('#test', ' text ') // second one
.verify.containsText('#test', ' text 1') // third one
will pass, because they each verify the specific information without the need for writing a loop. Nightwatch will look at the test element and serialize the elements into the string text text text 1
Now if you need a loop for other reasons this is all academic, but your original question seemed to be targeted at how to get the text information out, not necessarily how to execute one possible solution to the problem (which is writing a loop).
I created custom assertions - custom-assertions/hasItems.js with content:
exports.assertion = function hasItems(selector, items) {
this.message = `Testing if element <${selector}> has items: ${items.join(", ")}`;
this.expected = items;
this.pass = selectedItems => {
if (selectedItems.length !== items.length) {
return false;
}
for (let i = 0; i < selectedItems.length; i++) {
if (selectedItems[i].trim() !== items[i].trim()) {
return false;
}
}
return true;
};
this.value = res => res.value;
function evaluator(_selector) {
return [...document.querySelectorAll(_selector)].map(
item => item.innerText
);
}
this.command = cb => this.api.execute(evaluator, [selector], cb);
};
Related
I have a function that create LI and href element.
function getsubject(s) {
if (s <= 10) {
var sub = ['Choose subject','math', 'physics', 'chemistry', 'biology'];
} else {
var sub = [ 'Choose subject','math', 'physics', 'chemistry', 'biology', 'accounts', 'BMT'];
}
var subList = sub.length;
// var subl = document.getElementById("listul");
// var a = document.querySelectorAll('#listul .sli').length;
var elements = document.getElementsByClassName("sli");
while(elements.length > 0){
elements[0].parentNode.removeChild(elements[0]);
}
var liElem="";
for(var a =0;a<subList;a++){
liElem += '<li class="sli">'+sub[a]+'</li>';
}
document.getElementById('listul').innerHTML=liElem;
}
and HTML
<div class="list select_style">
<ol id="listul">
<li>choose subject</li>
</ol>
</div>
when I passing using created function without parameter that work.
when I passing parameters from function can't work.
Quotes
You must pass variables with quotes into onclick.
For now getsubject(s) appends string:
<li class="sli">chemistry</li>
You can note that onclick handler is invalid as it invokes ub function on undefined value chemistry.
But you can call functions on strings like this ub("chemistry").
So the solution is to place quotes around the values in the function call:
for(var a =0;a<subList;a++){
liElem += '<li class="sli">'+sub[a]+'</li>';
}
Template literal
Better approach (but not available on all browsers is to use ECMAScript template literals:
for(var a =0;a<subList;a++){
liElem += `
<li class="sli">
<a href="#" onclick="ub('${sub[a]}');">
${sub[a]}
</a>
</li>
`;
}
Template literal can be used as normal string with "" or '' quotes, but it can be multiline and could contain notation ${variable} which means to place value of variable in that place in the string.
JQuery approach
It's worth to consider something better than plain text html rendering.
The equivalent code using jQuery looks like this:
var liNode = $('<li class="sli"></li>');
$('').click(function() {
// Call ud from the handler when link is clicked
ud(value);
}).appendTo(liNode);
liNode.appendTo($('.sli'));
I have a list inside a anchor tag like this
<a href="#" class = "set_priority" data-value = "{{$candidate->id}}" style="text-decoration: none;">
#if($candidate->priority == "")
<li> Prioritize</li></a><br>
#elseif($candidate->priority == "yes")
<li> Deprioritize</li></a><br>
#endif
I have used entity   for styling purpose. The above code generate html tag according to the response from the server. So it might look either one of these
<li> Prioritize</li>
<li> Deprioritize</li>
I want to alert something when the list item is clicked. I don't know how to compare when   is used
$('.set_priority').on('click',function(){
var priority_value = $(this).first().text();
if (priority_value = "Prioritize") {
alert('he Prioritised');
}
else if (priority_value = "Deprioritize") {
alert('Prioritised');
}
});
It always alerts alert('he Prioritised'); whatever may be the condition.
You should use comparison operator instead assignment operator and use trim method to remove spaces.
$('.set_priority').on('click',function(){
var priority_value = $(this).first().text().trim();
if (priority_value == "Prioritize") {
alert('he Prioritised');
}
else if (priority_value == "Deprioritize") {
alert('Prioritised');
}
});
replace   with empty in temporary variable, then compare it. like:-
var priority_value = $(this).first().text();
var temp = priority_value.replace(/ /gi,'');
if (temp == "DePrioritize") {
alert('he Prioritised');
}
else if (temp == "Deprioritize") {
alert('Prioritised');
}
I think you should set the click handler like this
$('set_property li').on('click', function(){
var priority = $(this).first().text(); // This will give you the actual clicked element
// .... rest is same
});
I need to find all elements in a page by attribute value only (ignoring the key) using jquery.
Is there a way to do this easily?
Currently, I am just iterating on all elements in the page, on every property etc..
You can use $.expr, Element.attributes, Array.prototype.some()
$.expr[":"].attrValue = function(el, idx, selector) {
return [].some.call(el.attributes, function(attr) {
return attr.value === selector[selector.length - 1]
})
};
// filter element having attribute with `value` set to `"abc"`
$(":attrValue(abc)").css("color", "blue");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div title="abc">abc</div>
<div title="def">def</div>
<div title="ghi">ghi</div>
<div title="jkl">jkl</div>
Use brackets []
var ElementsWithAttributeKeyTest = $('[attributeKey="Test"]');
Or pass an object with the attribute name and value as parameter to this function:
var getElemsByAttribute = function(obj) {
if (obj) {
if (obj.attributeKey && obj.attributeValue) {
return $('[' + obj.attributeKey + '="' + obj.attributeValue + '"]');
}
}
}
var attrObj = {
attributeKey: 'data-color',
attributeValue: 'red'
}
getElemsByAttribute(attrObj).css('color', 'red');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<span data-color="red">Red</span>
<span data-color="red">Red</span>
<span data-color="green">Green</span>
<span data-color="blue">Blue</span>
<span data-color="red">Red</span>
<span data-color="green">Green</span>
If you want to search all attributes values you can use this small plugin:
$.fn.search_by_attr_value = function(regex) {
return this.filter(function() {
var found = false;
$.each(this.attributes, function() {
if (this.specified && this.value.match(regex)) {
found = true;
return false;
}
});
return found;
});
};
and you can use it like this:
$('*').search_by_attr_value(/^some value$/);
Based on this answer
You could define new function take as parameter the value you want to filter with (e.g get_elements_by_value(filter)), then inside this function parse all the elements of the page using $('*').each(), after that parse the attributes of every element el of those elements using attribute attributes like below :
$.each(el.attributes, function(){ })
Then inside the each loop you could make your condition and push the matched values with the passed filter inside matched[] that should be returned.
Check working example below, hope this helps.
function get_elements_by_value(filter){
var matched=[];
$('*').each(function(index,el) {
$.each(el.attributes, function() {
if( this.value===filter )
matched.push(el);
})
})
return $(matched);
}
get_elements_by_value('my_value').css('background-color','green');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div title="my_value">AA</div>
<div title="def">BB</div>
<input type='text' name='my_value' value='CC'/>
<p class='my_value'>DD</p>
<span title="test">EE</span>
I want to create pages with urls such as:
http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins
http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones
http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver
These particular URLs would all contain the exact same content (the "2015Aug24_Aug28" page), but would highlight all instances of the name tagged on to the end. For example, "http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones" would show every instance of the name "Billy Bones" highlighted, as if a "Find" for that name was executed on the page via the browser.
I imagine something like this is required, client-side:
var employee = getLastURLPortion(); // return "Billy_Bones" (or whatever)
employee = humanifyTheName(employee); // replaces underscores with spaces, so that it's "Billy Bones" (etc.)
Highlight(employee); // this I have no clue how to do
Can this be done in HTML/CSS, or is JavaScript or jQuery also required for this?
If you call the function
highlight(employee);
this is what that function would look like in ECMAScript 2018+:
function highlight(employee){
Array.from(document.querySelectorAll("body, body *:not(script):not(style):not(noscript)"))
.flatMap(({childNodes}) => [...childNodes])
.filter(({nodeType, textContent}) => nodeType === document.TEXT_NODE && textContent.includes(employee))
.forEach((textNode) => textNode.replaceWith(...textNode.textContent.split(employee).flatMap((part) => [
document.createTextNode(part),
Object.assign(document.createElement("mark"), {
textContent: employee
})
])
.slice(0, -1))); // The above flatMap creates a [text, employeeName, text, employeeName, text, employeeName]-pattern. We need to remove the last superfluous employeeName.
}
And this is an ECMAScript 5.1 version:
function highlight(employee){
Array.prototype.slice.call(document.querySelectorAll("body, body *:not(script):not(style):not(noscript)")) // First, get all regular elements under the `<body>` element
.map(function(elem){
return Array.prototype.slice.call(elem.childNodes); // Then extract their child nodes and convert them to an array.
})
.reduce(function(nodesA, nodesB){
return nodesA.concat(nodesB); // Flatten each array into a single array
})
.filter(function(node){
return node.nodeType === document.TEXT_NODE && node.textContent.indexOf(employee) > -1; // Filter only text nodes that contain the employee’s name.
})
.forEach(function(node){
var nextNode = node.nextSibling, // Remember the next node if it exists
parent = node.parentNode, // Remember the parent node
content = node.textContent, // Remember the content
newNodes = []; // Create empty array for new highlighted content
node.parentNode.removeChild(node); // Remove it for now.
content.split(employee).forEach(function(part, i, arr){ // Find each occurrence of the employee’s name
newNodes.push(document.createTextNode(part)); // Create text nodes for everything around it
if(i < arr.length - 1){
newNodes.push(document.createElement("mark")); // Create mark element nodes for each occurrence of the employee’s name
newNodes[newNodes.length - 1].innerHTML = employee;
// newNodes[newNodes.length - 1].setAttribute("class", "highlighted");
}
});
newNodes.forEach(function(n){ // Append or insert everything back into place
if(nextNode){
parent.insertBefore(n, nextNode);
}
else{
parent.appendChild(n);
}
});
});
}
The major benefit of replacing individual text nodes is that event listeners don’t get lost. The site remains intact, only the text changes.
Instead of the mark element you can also use a span and uncomment the line with the class attribute and specify that in CSS.
This is an example where I used this function and a subsequent highlight("Text"); on the MDN page for Text nodes:
(The one occurrence that isn’t highlighted is an SVG node beyond an <iframe>).
I used the following regex to replace all the matching url to create anchors with highlighted text:
(http://xyzcorp/schedules/(.*?)/)(.*?)( |<|\n|\r|$)
Debuggex Demo
The following code will replace all plain urls. If you don't need them to be replaced to links, just highlight them, remove the tags:
var str = "http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver ";
var highlighted = str.replace( new RegExp("(http://xyzcorp/schedules/(.*?)/)(.*?)( |<|\n|\r|$)","g"), "<a href='$1$3'>$1<span style='background-color: #d0d0d0'>$3</span></a>" );
The content of the highlighted string will be:
<a href='http://xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins'>http://xyzcorp/schedules/2015Aug24_Aug28/<span style='background-color: #d0d0d0'>Jim_Hawkins</span></a>
<a href='http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones'>http://xyzcorp/schedules/2015Aug24_Aug28/<span style='background-color: #d0d0d0'>Billy_Bones</span></a>
<a href='http://xyzcorp/schedules/2015Aug24_Aug28/John_Silver'>http://xyzcorp/schedules/2015Aug24_Aug28/<span style='background-color: #d0d0d0'>John_Silver</span></a>
UPDATE:
This function will replace the matching names from the input text:
function highlight_names( html_in )
{
var name = location.href.split("/").pop().replace("_"," ");
return html_in.replace( new RegExp( "("+name+")", "g"), "<span style='background-color: #d0d0d0'>$1</span>" );
}
One solution would be, after window is loaded, to traverse all nodes recursively and wrap search terms in text nodes with a highlight class. This way, original structure and event subscriptions are not preserved.
(Here, using jquery, but could be done without):
Javascript:
$(function() {
// get term from url
var term = window.location.href.match(/\/(\w+)\/?$/)[1].replace('_', ' ');
// search regexp
var re = new RegExp('(' + term + ')', 'gi');
// recursive function
function highlightTerm(elem) {
var contents = $(elem).contents();
if(contents.length > 0) {
contents.each(function() {
highlightTerm(this);
});
} else {
// text nodes
if(elem.nodeType === 3) {
var $elem = $(elem);
var text = $elem.text();
if(re.test(text)) {
$elem.wrap("<span/>").parent().html(text.replace(re, '<span class="highlight">$1</span>'));
}
}
}
}
highlightTerm(document.body);
});
CSS:
.highlight {
background-color: yellow;
}
$(function() {
// get term from url
//var term = window.location.href.match(/\/(\w+)\/?$/)[1].replace('_', ' ');
var term = 'http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones/'.match(/\/(\w+)\/?$/)[1].replace('_', ' ');
// search regexp
var re = new RegExp('(' + term + ')', 'gi');
// recursive function
function highlightTerm(elem) {
var contents = $(elem).contents();
if(contents.length > 0) {
contents.each(function() {
highlightTerm(this);
});
} else {
// text nodes
if(elem.nodeType === 3) {
var $elem = $(elem);
var text = $elem.text();
if(re.test(text)) {
$elem.wrap("<span/>").parent().html(text.replace(re, '<span class="highlight">$1</span>'));
}
}
}
}
highlightTerm(document.body);
});
.highlight {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>
<div class="post-text" itemprop="text">
<p>I want to create pages with urls such as:</p>
<pre style="" class="default prettyprint prettyprinted">
<code>
<span class="pln">http</span>
<span class="pun">:</span>
<span class="com">//xyzcorp/schedules/2015Aug24_Aug28/Jim_Hawkins</span>
<span class="pln">
http</span>
<span class="pun">:</span>
<span class="com">//xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones</span>
<span class="pln">
http</span>
<span class="pun">:</span>
<span class="com">//xyzcorp/schedules/2015Aug24_Aug28/John_Silver</span>
</code>
</pre>
<p>These particular URLs would all contain the exact same content (the "2015Aug24_Aug28" page), but would highlight all instances of the name tagged on to the end. For example, " <code>http://xyzcorp/schedules/2015Aug24_Aug28/Billy_Bones</code>
" would show every instance of the name "Billy Bones" highlighted, as if a "Find" for that name was executed on the page via the browser.</p>
<p>I imagine something like this is required, client-side:</p>
<pre style="" class="default prettyprint prettyprinted">
<code>
<span class="kwd">var</span>
<span class="pln"> employee </span>
<span class="pun">=</span>
<span class="pln"> getLastURLPortion</span>
<span class="pun">();</span>
<span class="pln"></span>
<span class="com">// return "Billy_Bones" (or whatever)</span>
<span class="pln">
employee </span>
<span class="pun">=</span>
<span class="pln"> humanifyTheName</span>
<span class="pun">(</span>
<span class="pln">employee</span>
<span class="pun">);</span>
<span class="pln"></span>
<span class="com">// replaces underscores with spaces, so that it's "Billy Bones" (etc.)</span>
<span class="pln"></span>
<span class="typ">Highlight</span>
<span class="pun">(</span>
<span class="pln">employee</span>
<span class="pun">);</span>
<span class="pln"></span>
<span class="com">// this I have no clue how to do</span>
</code>
</pre>
<p>Can this be done in HTML/CSS, or is JavaScript or jQuery also required for this?</p>
</div>
Demo: http://plnkr.co/edit/rhfqzWThLTu9ccBb1Amy?p=preview
Suggest me any good mustache doc. Also i want to know in a mushtach loop how do i get the count or the loop no. I mean how can i do a for loop in mustache.
In the below code i wish to change the id in every loop
<script src="http://github.com/janl/mustache.js/raw/master/mustache.js"></script>
<script>
var data, template, html;
data = {
name : "Some Tuts+ Sites",
big: ["Nettuts+", "Psdtuts+", "Mobiletuts+"],
url : function () {
return function (text, render) {
text = render(text);
var url = text.trim().toLowerCase().split('tuts+')[0] + '.tutsplus.com';
return '' + text + '';
}
}
};
template = '<h1> {{name}} </h1><ul> {{#big}}<li id="no"> {{#url}} {{.}} {{/url}} </li> {{/big}} </ul>';
html = Mustache.to_html(template, data);
document.write(html)
</script>
<body></body>
You can't get at the array index in Mustache, Mustache is deliberately simple and wants you to do all the work when you set up your data.
However, you can tweak your data to include the indices:
data = {
//...
big: [
{ i: 0, v: "Nettuts+" },
{ i: 1, v: "Psdtuts+" },
{ i: 2, v: "Mobiletuts+" }
],
//...
};
and then adjust your template to use {{i}} in the id attributes and {{v}} instead of {{.}} for the text:
template = '<h1> {{name}} </h1><ul> {{#big}}<li id="no-{{i}}"> {{#url}} {{v}} {{/url}} </li> {{/big}} </ul>';
And as an aside, you probably want to include a scheme in your url:
url : function () {
return function (text, render) {
text = render(text);
var url = text.trim().toLowerCase().split('tuts+')[0] + '.tutsplus.com';
return '' + text + '';
//---------------^^^^^^^
}
}
Demo: http://jsfiddle.net/ambiguous/SFXGG/
Expanding on #mu's answer, you could also keep an index in the data object and have the template refer to it and the function increment it. So you wouldn't need to add i to each item.
see demo : http://jsfiddle.net/5vsZ2/