Test async page changes with the intern - javascript

Lets say I have a button and a div element on a page.
<button /><div>old text</div>
By clicking on button, javascript asynchronously change the text of the div (i.e. after ajax call). How can i test it with intern?
This does not work:
function testButtonClick() {
return command
.findByTagName('button')
.click()
.end()
.findByTagName('div')
.getVisibleText()
.then(function(text) { assert.strictEqual(text,'new text')}); //errror, text is 'old text'
}
If I add .sleep(5000) after .end(), then it works Ok (because my async request is usually done in 5 seconds). But I do not want to wait so long, because the async is usually finished much earlier.
But using sleep with lower time value, I risk that it will be tested before the request is finished.
Is there a better way?

The most efficient solution would be to use pollUntil, something like:
return command
.findByTagName('button')
.click()
.end()
.then(pollUntil(function () {
var el = document.getElementByName('div');
if (el && el.textContent === 'new text') {
return true;
}
}, 5000));
Poll until will run a function in the browser context repeatedly until it returns a non-null, non-undefined answer, or until it times out. In the snippet above the polling function returns true if the element exists and has the expected text, or undefined otherwise. It will timeout after 5000ms, but it will end as soon as the expected text shows up.

Related

What is the usage of setTimeout in this code

In the book, author uses "setTimeout" to reset the form in HTML page.But I didn't find this usage in the JS document.So, why function"setTimeout()" can reset the form?
//reset
$("#reset").click(function(){
setTimeout(function() {
countChecked();//initial
$("select").change();//initial
},0);
});
function countChecked() {
var n = $("input:checked").length;
$("div").eq(0).html("<strong>"+n+" elements are selected!</strong>");
}
$("select").change(function () {
var str = "";
$("select :selected").each(function () {
str += $(this).text() + ",";
});
$("div").eq(1).html("<strong>you have selected:"+str+"</strong>");
}).trigger('change');
So, why function"setTimeout()" can reset the form?
It doesn't, it just waits to run the code within it until after the event trigged by the button click has been processed.
You haven't shown the HTML, but I'm guessing that the id="reset" button is also type="reset", which does reset the form (standard HTML functionality, no scripting required). So by waiting to call countChecked until after the reset, the code shows the state once the reset is complete.
The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds.
I see the time is set to 0 so it does not count.
Window setTimeout() Method: https://www.w3schools.com/jsref/met_win_settimeout.asp
setTimeout(function,0) or setTimeout(function), the zero is optional, both place the code to be executed at the end of the current code being executed, and after any updates the browser may have pending.
This was useful before ES6 to deblock the browser for scripts that took a long time to finish. Just use setTimeout for each iteration of the loop without a delay.
For example
setTimeout(function() { console.log(2) }); // will log 2 after this script finishs
console.log(1)
Will place into the console log 1 then 2, not 2 then 1.
I don't see the point of doing
countChecked();//initial
$("select").change();//initial
after the code which is shown, has finished?

Proper way to limit wait time on selenium element search

My nightwatch/selenium test code looks for elements in the page that may not exist using code such as
browser.elementIdElement(ELEMENT,'class name', 'myclass', function(r)
{ if (r.status == 0) console.log('found match')
else console.log('did not find match')
})
If the element is found, the callback is invoked quickly (< 50ms), but if no element matches, the callback takes much longer (>1000ms). I have to do this hundreds of times and there are only a few elements that match the search criteria, so it adds a significant amount of time to a test run.
I would like to limit the time selenium spends searching for elements. I tried using the selenium timeoutsImplicitWait() function, e.g.,
browser.timeoutsImplicitWait(250)
.elementIdElement(ELEMENT,'class name', 'myclass', function(r)
{...})
but it doesn't affect performance. What is the correct method for limiting element search time?
Perhaps I am misunderstanding your problem; both of these patterns works well for me:
client
.useXpath().waitForElementPresent(selector, this.timeout)
.useCss().waitForElementPresent(selector, this.timeout)
this.timeout is set in the prototype of the base test case.
util.inherits(MyExampleBaseClass, Base);
MyExampleBaseClass.prototype = {
before: function (client) {
// call super-before
Base.prototype.before.call(this, client);
this.timeout = 250;
},
after: function (client, callback) {
// call super-after
Base.prototype.after.call(this, client, callback);
},
// Note: This method will not be mistaken by nightwatch for a step because
// it is not enumerable (since it's on the prototype)
getSiteURL: function () {
return "http://www.urlundertest.com/";
}
};
The following code for checking the visibility and continue even if there is no match
browser.waitForElementVisible('selector',timeout,false);
or this for the existence :
browser.waitForElementPresent('selector',timeout,false);
According to nightwatch api,
By the default if the element is not found the test will fail. Set this to false if you wish for the test to continue even if the assertion fails.To set this globally you can define a property abortOnAssertionFailure in your globals.
For more detailed explanation, check here:
http://nightwatchjs.org/api/#waitForElementVisible

Query about setTimeout()

Since past couple of months, I have been doing a lot of JS, but I have never really learned it in a college/from a book etc.
Here is a question:
$scope.selectTab = function($index, tab) {
$scope.template = $scope.templates[$index];
$scope.data.tabSelected = tab;
setTimeout(function() {
console.clear();
console.log($scope.template);
console.log(document.getElementById("SomeDiv"))
console.log("Going to draw now...")
draw_analytics($scope); // Draw on SomeDiv
}, 0);
}
while the above code works; but the below one doesn't.
$scope.selectTab = function($index, tab) {
$scope.template = $scope.templates[$index];
$scope.data.tabSelected = tab;
console.clear();
console.log($scope.template);
console.log(document.getElementById("SomeDiv"))
console.log("Going to draw now...")
draw_analytics($scope);
}
I am using AngularJS for some tabbing thing, but that is irrelevant I guess.
In the top code, when I try to get SomeDiv dom, I actually get it's HTML content, while in the bottom code, null is returned.
When you use settimeout with an anonymous function, it doesn't execute it right away. It adds it to a queue to be executed after the current events finish.
This is opposed to running it without settimeout, which just executes it immediately.
If I had to guess, "SomeDiv" is being created or manipulated with the tab change. So running the code right away may cause a situation where the code executes before the div is actually created. While calling "settimeout" will wait until the current events complete before executing. Although it looks like it's inline with the event, it's really just scheduling the anonymous function to run at the end.
I created a fiddle to illustrate what I'm talking about.
http://jsfiddle.net/pS54r/
The first button using settimeout, the second button doesn't.
$('#clickme').click(function () {
WriteToDiv('First Button 1');
setTimeout(function () {
WriteToDiv('First Button 2');
}, 0);
WriteToDiv('First Button 3');
});
$('#clickme2').click(function () {
WriteToDiv('Second Button 1');
WriteToDiv('Second Button 2');
WriteToDiv('Second Button 3');
});

CoffeeScript - What does this strange "Return" occurrence mean?

Consider the following CoffeeScript:
$ ->
if localStorage["Flag1"] isnt "Done"
localStorage["Flag1"] = "Done" # Flagged on first page loading
$(".start").click ->
if localStorage["Flag2"] isnt "Done"
localStorage["Flag2"] = "Done" # Flagged on first click interaction
Which compiles into:
$(function() {
if (localStorage["Flag1"] !== "Done") {
localStorage["Flag1"] = "Done";
}
return $(".start").click(function() {
if (localStorage["Flag2"] !== "Done") {
return localStorage["Flag2"] = "Done";
}
});
});
There are two strange occurrence of "return" being planted into the rendered JavaScript. What do they do, and how will they affect the running of the script? Thanks!
They won't affect the running of your script. The first return will return $(".start") (since the jQuery click method returns an instance of jQuery) from the DOM ready event handler. Since it's a callback that runs at a certain point, you can't really do anything with that return value.
The second return will return "Done", after setting the localStorage property, but again, since it's returning from a callback (a click event handler this time) you won't be able to do anything with the returned value.
I believe CoffeeScript will return the value of the last expression in each function, which is why you see those return statements in the output. From the docs:
Even though functions will always return their final value, it's both
possible and encouraged to return early from a function body writing
out the explicit return (return value), when you know that you're
done.

Resetting setTimeout object if exists

When somebody clicks my checkboxes, from a long list of checkboxes, I want to show the number of selected checkboxes in a little popup element. My problem is, the little popup element should disappear 5 seconds after the last click, which is OK for one checkbox being clicked, but if I quickly check 5 boxes, the timer is still set on the first box, resulting in the popup element disappearing too quickly.
As you can see in my function, I've tried using the clearTimeout(timeoutName) function but have experienced some troubles applying it. The console log states that the clearTimeout(timeoutName) is undefined, which I can understand: the setTimeout hasn't even started yet.
How can I check that the timer exists before I clear it? Or is this really not the best method? When a checkbox is checked (this function runs) there could be a timer running but sometimes there could not be.
$('.name_boxes').live('click', function() {
var checked_count = $('.name_boxes:checked').length;
// other stuff
clearTimeout(hide_checked_count_timer); // if exists????????
$(".checked_count").hide();
$(".checked_count").text(checked_count+" names selected");
$(".checked_count").show();
hide_checked_count_timer = setTimeout(function() {
$(".checked_count").hide();
},5000);
});
Any help gratefully received...
Just declare the timer variable outside the click handler:
var hide_checked_count_timer;
$('.name_boxes').live('click', function() {
var checked_count = $('.name_boxes:checked').length;
// other stuff
clearTimeout(hide_checked_count_timer); // if exists????????
$(".checked_count").hide();
$(".checked_count").text(checked_count+" names selected");
$(".checked_count").show();
hide_checked_count_timer = setTimeout(function() {
$(".checked_count").hide();
},5000);
});
http://jsfiddle.net/kkhRE/
Considering .live has been deprecated, you should be delegating the event with .on instead:
// Bind to an ancestor. Here I'm using document because it an
// ancestor of everything, but a more specific ancestor
// would be preferred.
$(document).on('click', '.name_boxes', function() {
// ...
});
Q. The console log states that the clearTimeout(timeoutName) is undefined, which I can understand: the setTimeout hasn't even started yet.
A. The clearTimeout() function's return value is undefined regardless of whether there was a timeout to be cleared. It doesn't have a concept of "success" that can be tested. If there is a queued timeout associated with the id you pass then it will be cleared, otherwise nothing happens.
Q. How can I check that the timer exists before I clear it?
You can't, at least not in the sense of there being some registry of outstanding timeouts that you can query. As you already know, the .setTimeout() function returns an id for the timeout just queued, and you can use that id to clear it before it runs, but there is no way to test whether it has already been run. The id is just a number so the variable that you saved it in will continue to hold that number even after the timeout has either run or been cleared.
It does no harm at all to call clearTimeout() with an id for a timeout that already ran - basically if the timeout for that id is in the queue it will be cleared otherwise nothing will happen.
The easiest way to test "Is there an outstanding timeout that hasn't run yet" is to set the variable holding the timerid to null when the timeout runs, i.e., within the function you queued:
var timerid = setTimout(function() {
timerid = null;
// other operations here as needed
}, 5000);
// in some other code somewhere
if (timerid != null) {
// timer hasn't run yet
} else {
// timer has run
}
The variable you save the timerid in needs to be in a scope that can be accessed both where you set it and where you test it, i.e., don't declare it as a local variable within an event handler.
You can use the power of short-circuit operators
hide_checked_count_timer && clearTimeout(hide_checked_count_timer);
The right-hand statement will only run if the left-hand variable is not undefined.
to check if it exists use;
if (typeof timerid == 'undefined')
{
//timer has not been set so create it
timerid = setTimeout(function(){ var something = true;}, 5000);
}

Categories

Resources