phantomjs/casperjs count DOM elements - javascript

I would like to count the number of, let's say, div elements with 'nice' class. I've got the selector div.nice, but don't know which casperjs class/method to use.
There is a tester.assertElementCount method in fact, but is there anything that simply returns the number of elements?

Just
document.querySelectorAll("div.nice").length

If you can use jquery its fairly simple:
var count = $('div.classname').length;
Found an SO Post that seems to explain using jquery with casperjs, I have no experience with casperjs so I can't help much there.

One of the examples for CasperJS 1.1-beta3 involves checking the number of Google search results for CasperJS. It references __utils__.findAll(), which takes a selector as its argument. It allows you to check the number of items returned using the length property available to any JS object:
test.assertEval(function() {
return __utils__.findAll("h3.r").length >= 10;
}, "google search for \"casperjs\" retrieves 10 or more results");
I've never tried it, but it seems like this utility function can be used outside a conditional, and it will allow you to report the number of elements without using jQuery, as a previous answer recommended.

Casper provides getElementsInfo, you can use the attribute length to get the number of elements.
e.g.
casper.getElementsInfo('myElement').length

you also can use assertElementCount to assert the count of the elment
test.assertElementCount("div.nice", 1)

I did not find the answers above to be helpful to my cause.
I think the goal was to count the number of elements without having to evaluate the js code in the page context, which could be frustrating overtime and have conflicting variables and functions.
Instead, it would be nice to leverage the casper automation context. This can be done with a combination of ".exists()" and the css psuedo-selector ":nth-of-type(i)"
The code below does this...
var counter = 1; //set to one, for css selector setup
casper.then(function() { //wait your turn
//loop through our element
while(casper.exists( 'div span:nth-of-type(' + counter + ')' )) {
counter++; //count the results
}
});
You could make this a function and pass in all the arguments, or just copy and paste it as a step.
Best part, you could follow it with a repeat statement for a pretty cool loop.
casper.then(function(){
this.repeat(counter, function() {
console.log("Another one - item #" + counter);
});
});

Related

How do I use Protractor to loop through a set of elements and test if they are clickable

I need to test if a list of buttons on the page are displayed and enabled First I gather all of the elements with elements.all
allEmployeeOptions = element.all(by.css('[role=option]'));
Then I attempted to use the .each function to loop through them and test if they are displayed (I will add isEnabled too).
testAllOptionsClickable(){
//this.actions.click();
browser.sleep(3000);
this.allEmployeeOptions.each(function(elm){
expect(elm.isDisplayed).toBe(true);
});
This doesn't seem to be working, I get "Expected function to be true" repeated 10 times for each element.
My best guess is that it is having trouble resolving the looping promise, but this is my first time writing a loop like this in Protractor. It's also possible .each is not the right approach and a for loop would be better.
Any help is appreciated.
.each is the right approach, I would not use a for loop to iterate over elements. Your problem is just missing parenthesis, isDisplayed() is a function so you simply need to change your assertion to expect(elm.isDisplayed()).toBe(true);
Also, for what it's worth I would add another assertion. Being displayed does not mean an element is necessarily clickable, you should consider adding a check for isEnabled() as well.
You are doing it right. But protractor has it's method to ckeck if an element is clickable.
example:
it('....xxxx.', function() {
elems=$$('.items.item li'); // use your css locator
var EC = protractor.ExpectedConditions;
elems.each(async function(elem){
browser.wait(EC.elementToBeClickable(elem), 5000);
// perform other action
let elmtxt= await elem.getText();
console.log("text: "+elmtxt);
//expect(elem.isDisplayed()).toBe(true); /toBeTruthy();/
});

Javascript Find User

I have the below javascript to get the UserID from a online form. This script will go through IE DOM Explorer to find the valued. But when I run the script, it is totally ignoring my "If" statement. It is just providing a value for "NewAuthUserID", without considering the "if".
(function () {
var NewAuthUserID = "";
var UserId = $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if ('tr.background-highlight:contains("NEW AUTHORIZED INDIVIDUAL PROFILE:"') {
var NewAuthUserID = $('td:contains("User ID:")+td:eq(2)').text();
};
alert(UserId);
alert(NewAuthUserID)
})();
Firstly, I'd suggest to check out how the if statement works: https://www.w3schools.com/js/js_if_else.asp
You need the if statement conditional to return true or false. Right now you're TRYING to use jquery to select things but even that has a syntax issues. Not only that but once the syntax is fixed it STILL won't do what you're attempting to do because you're putting something that will always evaluate to true as the conditional. That jquery selector just returns a function, not a boolean like it looks like you're intending to do. Try this:
(function(){
var NewAuthUserID = "";
var UserId=$('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if($('tr.background-highlight').text() == "NEW AUTHORIZED INDIVIDUAL PROFILE:")){
var NewAuthUserID=$('td:contains("User ID:")+td:eq(2)').text();
}
alert(UserId);
alert(NewAuthUserID)
})();
Notice how I'm snagging the text that you're trying to test against with jquery and expressing it with a conditional instead? In this manner, it will return the boolean: true/false which is what you need to get the if statement to trigger.
Also if you check your syntax, you were missing the $() wrapper around your if statement, but you have a string that looked like it was trying to snag text via jquery.
I suggest formatting your code a bit, this always helps to debug.
The problem is you are trying to use a jQuery selector in your if statement, but you didn't include the $ to evaluate jQuery. It's just evaluating a string, wich results in TRUE (basically doing this: if(true)), so the code block is executed.
Try this instead:
javascript: (function() {
var NewAuthUserID = "";
var UserId = $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td').text();
if ($('tr.background-highlight:contains("NEW AUTHORIZED INDIVIDUAL PROFILE:"').length > 0) {
var NewAuthUserID = $('td:contains("User ID:")+td:eq(2)').text();
};
alert(UserId);
alert(NewAuthUserID)
})();
EDIT: I added the length > 0 check on the returned object. It's possible to accomplish this with OP's code, he was just missing those two pieces. :contains is not the same as .text() ==.
Off topic response:
The way you manage/select your nodes may require a lot of maintanance in the future and is prone to errors.
For example: tr.background-highlight:contains("REQUESTER PROFILE") + tr
In words: Get me the table-row after a table-row with hilighted background, that contains "REQUESTER PROFILE".
What if you'll have to add a row in between them? what if you'll need to select the row, wether it is hilighted or not? what if further rows will be hilighted in the future, so that this selector ain't uniqu anymore? what if the label changes? maybe even the language? ...
In each of these cases you'll have to revisit (potentially all) your jquery selectors, just because some minor layout changed.
That's not very reliable.
Will you remember that when you'll get asked to do these changes? Maybe someone else will have to do these changes, will he/she know what to look for?
Tell me, do you remember the details/implications/quirks of the work you've done a week ago? not to speak about your work from a few months ago.
Better:
Use "unique" identifier to, well, identify your nodes by their role; and I'm not talking about IDs. Unique within their specific context.
The easiest way would be to use css-classes. Annotating the rows/cells so you can select the very same field as $('.ref-requester-provile .ref-user-id')
This is way more reliable and future-proof than your bulky $('tr.background-highlight:contains("REQUESTER PROFILE") + tr').children('td:contains("User ID:")+td') where your JS needs to know every little detail of your template/markup, and needs to be adapted with every little change.
Why did I prepend these classes with ref-? to distinct them from classes that are meant for styling
If you don't need to style these nodes and need these identifyer solely to reference them in your JS, I'd rather use a data-attribute. Why? Let's sum it up with:
performance: when you need to add/remove these marker; avoid unnecessary render-cycles
A cleaner seperation between style and code: classes are primarily for styling, but we don't style here.

jQuery: window.opener makes a difference

I'm working on localhost (so would expect to not have any domain-related probs as here).
On a page I'm using a bit of JS to modify the content of a span in the opening-window. It does not work.
When checking my code to find the control, it works (using FF dev-tools calling my Increment-function or checking the console.log-output): $('#uploads_Count')returns an object of type HTMLSpanElement. However, trying to access the same control from an opened window's console with window.opener.$('#uploads_Count'), this returns an HTML-Document, seemingly the entire page. Why is this not working, what am I missing here?
Here is function that is supposed to increment the counter contained in the span whose id is given as argument:
function Increment(ctrl)
{
var gef = $("#" + ctrl);
if (!gef) // did not find control, maybe on opener?
{
gef = window.opener.$("#" + ctrl);
}
console.log(gef);
cnt = parseInt(gef.text() , 10);
cnt++;
gef.text(cnt);
}
The HTML is trivial:
<span id="uploads_Count">0</span>
If $(selector) returns an element (such as HTMLSpanElement), rather than a collection of elements (would look like [<span id="uploads_Count"></span>] in most dev tools), then you're not calling jQuery.
Dev tools in A-grade browsers tend to introduce $ as a selector function. It is available in the developer console only.
If window.jQuery exists, then it's likely that jQuery.noConflict() was called, in which case you should use window.opener.jQuery.
Found it!
The way I checked if the control was found, was wrong. Instead of if (!gef)I should have used if (!gef.length). Found the explanation here.

Greasemonkey, replace every occurence of string every second

i try to figure out a greasemonkey script that replaces every onmousedown on a site with an ondblclick. And i want it to constantly update, like every 1,5 Seconds, because the page refreshes using AJAX.
This is the script i came up with, but it doesn't seem to be working.
window.setInterval(document.body.innerHTML= document.body.innerHTML.replace('onmousedown','ondblclick');,1500);
The page it should work with is internal use only. But a good example would be the google search, where onmousedown is used for the links of the results to swap out the URL before you click it.
I also tried it without the semicolon after the document.body.innerHTML.replace.
I'm really new to JavaScript, but since i'm the only one in the company who can code, this one is stuck with me.
Any help would be appreciated.
Also, a small "side question"
Do i have to use #exclude, or is it enough to only use #include internal.companysite.tld* so it will only work on this site ?
A direct answer: you need to supply a function to setInterval - and it's best to set a variable so that you can later cancel it with clearInterval() if necessary.
function myF(){document.body....;}
var myIntv = setInterval(myF, 1500);
You could also do it using an anonymous function in one line as you're trying to do... do that this way:
var myIntv = setInterval(function(){document.body....;}, 1500);
I wouldn't suggest this as the solution to your problem. What it sounds like you want to do is manipulate the active DOM - not really change the UI. You likely need something like this:
var objs = document.getElementsBy__(); // ById/ByName/etc - depends on which ones you want
for (var i in objs){objs[i].ondblclick = objs[i].onmousedown;objs[i].onmousedown = undefined;} // just an example - but this should convey the basic idea
Even better, if you can use jQuery, then you'll be able to select the proper nodes more easily and manipulate the event handlers in a more manageable way:
$(".class.for.example").each(function(){this.ondblclick = this.onmousedown;this.onmousedown = undefined;}); // just an example - there are multiple ways to set and clear these

Improve performance of my jQuery selector - remove the use of jquery.find

I have this code:
$item = $row.find('td[data-id="' + id + '"]');
$row is a jquery reference to a table row. Is there any way I can optimise this code? I don't really want to have to use the find function unless I have to. I was wondering if I could do something like:
$item = $($row + 'td[data-id="' + id + '"]');
but that doesn't seem to work. Is there any way in which I can improve this code? In my code this is called many times and seems to be causing issues in IE.
without using find explicitly
$item = $('td[data-id="' + id + '"]',$row);
though .find() is faster then then what i suggested
This is a very small piece of code to try and optimise. You say it's called many times. You should look at making one call to extract all the cells you want, then operating on the resulting collection. Some HTML would help to see if that's really feasible.
In your particular case you already have the first selection and wishes to search within that result
you can either do
//passing the $row as the context for the selection
$("'td[data-id="' + id + '"]'",row);
or
$row.find('td[data-id="' + id + '"]')
However the former will actually call the latter after passing a few ifs so the latter will always outperform the former
using .find()is generally a good thing when it comes to performance. In compound selectors like
$("table td div")
The div's are found and then filterended based on their parents and the parent of their parent after that. using find you can force the selction to evaluate from left to right instead
$("table").find("td").find("div")
The latter will executed faster assuming you have more divĀ“'s thantable`'s

Categories