Why is Protractor running every line of code immediately?
So I have a webpage that is not written in angular. That I need my selenium based automation to hit. I have used selenium webdriver-js code to hit it. Example below. Once you login, you are taken to a page with 3 questions. The order of the questions are randomized each time you login. So you may never get the same questions in the same order each time you login.
Question 1) What is your name?
Question 2) What time is it?
Question 3) Wy are you here?
The answers to each question are the last word of the question.
Answer 1) name
Answer 2) it
Answer 3) here
So what I was thinking an easy way to solve this problem is to create an if conditional statement
var foo = browser.driver.findElement(By.id('question1')).getText();
if(foo == What is your name?) {
browser.driver.findElement(By.id('answer1')).sendKeys('name');
}
else {
blah
}
and so forth... etc...
But the problem I am running into is that Protractor immediately runs the if statement before it gets to that point. In the example below, the console immediately prints out the console log 'this sucks', because it runs through the if statement immediately without going through the first steps to get to the page and then checking.
this.foo_test = function() {
console.log('starting foo test');
browser.driver.get('http://my-test-url.com/');
browser.sleep(3000);
browser.driver.findElement(By.id('login')).click();
browser.sleep(3000);
browser.driver.findElement(By.id('user')).sendKeys('user');
browser.driver.findElement(By.id('login_button')).click();
browser.sleep(3000);
console.log('getting variable');
var foo = browser.driver.findElement(By.id('question1')).getText();
console.log(foo);
if (foo == 'What was the name of your first pet?') {
console.log('this is cool');
}
else{
console.log('this sucks');
}
};
Protractor builds on WebdriverJS, which uses an implicit-promise-queuing style of programming. See:
https://github.com/angular/protractor/blob/master/docs/control-flow.md
What that means is that each statement in a protractor test should be read as enqueuing a promise, not as actually executing. So for example, the line:
browser.driver.findElement(By.id('question1')).getText()
Does not return text, but returns a promise to return text. You must pass this promise to the other promise-expecting APIs, or provide a direct handler with .then().
The expect call you see in Protractor tests has been modified to wait for a promise to resolve. So something like:
expect(name.getText()).toEqual('Jane Doe');
Is actually enqueuing a promise to compare the result of the promise on the left to the value on the right.
I don't know much about Protractor specifically but this sounds like an issue of not recognizing asynchronous code. If the first assignment statement is asynchronous, then the rest of the code will run without waiting for it to complete. Hence, the values you expect will not be there when you try to test for them in the IF statement.
Your best bet is to run the rest of the code in a callback or promise .
It appears your page is still loading even though selenium thinks it is complete. This happens alot with dynamic/asynchronous pages.
browser.sleep() is not really appropriate, you never really know how long you need to wait for.
You can investigate the class WebDriverWait, which allows you to wait for an element to appear, or timeout.
Wait<WebDriver> wait = new WebDriverWait(driver, 50); // timeout is 50 secs
wait.until(new Function<WebDriver, Boolean>() {
public Boolean apply(WebDriver driver) {
return (driver.findElement(By.id('question1'))).isDisplayed();
}
});
You can also try running some javascript to check the document ready status :
Wait<WebDriver> wait = new WebDriverWait(driver, 60); // timeout is 60 secs
wait.until(new Function<WebDriver, Boolean>() {
public Boolean apply(WebDriver driver) {
String docReady = "";
Boolean rc = true;
if (null != ((RemoteWebDriver)driver).getSessionId()) {
docReady = String.valueOf(((JavascriptExecutor) driver).executeScript("return document.readyState"));
rc = docReady.equals("complete");
}
return rc;
}
});
Related
I am in a situation where I need to use for loop and If else block in cypress
Scenario:
Once I login to an application, I need to read an element's text which is rounded in the below screenshot.
This element will appear within 20-90 seconds after I log in, when I refreshed the screen. so I need to write something like this, wait for element, if it appears reads the text and returns the value, if not wait for 10 seconds reload the page and do the process again.
function waitAndreload() {
for (let i = 0; i < 10; i++) {
cy.get("#ele").then(ele => {
if (ele.text()) {
return ele.text();
} else {
cy.wait(10000);
cy.reload();
}
});
}
}
How to write this in cypress, as cypress won't support if-else or for loops
Below is a technical solution, but first is want to explain what I believe is a better solution.
Adding to #dwelle comment, it seems like what you're trying to do is not a best-practice in terms of the the design of the test. Tests should be designed to be deterministic and should control all relevant inputs that may affect the expected result.
More specifically, is this text something that a real user should see and use, or only something that the developers put for debugging or testing purposes? If it's for a real user, then what determines if it should appear or not? (Is it purely random?, If so, see below) if it's for testing or debugging purposes, talk to the developers and come up with a better solution in which you can control whether this text appears or not directly. If it's something that the user should see and it's not random, then consider what conditions should be met in order for the text to appear, and design the test in a way that makes this condition true, either by controlling the actual necessary preconditions or by using mocks to simulate that condition. Again, I recommend that you consult with the developers to help you find the best approach.
In case it is "purely" random, then ask the developers to provide a way for you to specify the seed of the random generator, and then you'll be able to control it too.
As promised, in case you still want the technical solution for the specific problem, without redesigning the test, then there trick is to use recursion. Something like this:
function getEnvironment() {
function getEnvironmentInternal(retires) {
if (retires == 0)
throw "text didn't appear after the specified retires";
return ele.text().then(text => {
if(text)
return cy.wrap(text);
cy.wait(10000);
cy.reload();
return getEnvironmentInternal(retires-1);
});
)};
return getEnvironmentInternal(10);
}
// usage:
getEnvironment().then(text => {
// do something with text...
}
I wrote my own helper command for checking repeatedly until condition is fulfilled.
/**
* Waits until call to cb() resolves to something truthy.
*
* #param message {string} Error message on timeout
* #param cb {() => Cypress Command "promise"}
* Callback for checking if condition is met should return cypress command cy.xxxxxx.then()
* which resolves to undefined if polling should continue. Throwing an error aborts before
* waiting for timeout to complete.
*/
Cypress.Commands.add('waitFor', (message, cb, errorReporterCb = null, timeoutMs = 5000) => {
const startTime = new Date().getTime();
const giveupTime = startTime + timeoutMs;
const startTimeout = 5;
const ctx = {};
const errorReporter =
errorReporterCb ||
(err => {
throw err;
});
function checkCb(timeout) {
const currentTime = new Date().getTime();
if (currentTime > giveupTime) {
const err = new Error(`Timeout while waiting for (${currentTime - startTime}ms): ${message}`);
errorReporter(err, ctx);
} else {
cy.wait(timeout);
return cb(ctx).then(result => {
if (result === undefined || result === false) {
return checkCb(timeout * 2); // always wait twice as long as the last time
} else {
return result;
}
});
}
}
return checkCb(startTimeout);
});
With this you can implement polling loop like:
cy.waitFor(
'reload page until #ele contain text',
() => cy.reload().get("#ele").then(ele => ele.text() ? ele.text() : undefined),
null, 60000);
I would say using a for loop for something like this and refreshing is an anti-pattern. It looks like you're waiting for the text to show up in the element, not the element itself.
If so, can you stub the response to the server so it comes back right away? If that doesn't work, just do a cy.wait('#<whatever you aliased your response as>') until the call is completed
So it seems you just want to wait that element is appeared and then take text value.
So something like cy.get('#ele', {timeout: 60000}).should('exist').invoke('text').then(text => ...work with text value)
Assertions in cypress have built-in retry mechanism, so if it fails before timeout expire - it will retry previous command.
You can't use while/for loops with cypress because of the async nature of cypress. Cypress doesn't wait for everything to complete in the loop before starting the loop again. You can however do recursive functions instead and that waits for everything to complete before it hits the method/function again.
Here is a simple example to explain this. You could check to see if a button is visible, if it is visible you click it, then check again to see if it is still visible, and if it is visible you click it again, but if it isn't visible it won't click it. This will repeat, the button will continue to be clicked until the button is no longer visible. Basically the method/function is called over and over until the conditional is no longer met, which accomplishes the same thing as a loop, but actually works with cypress.
clickVisibleButton = () => {
cy.get( 'body' ).then( $mainContainer => {
const isVisible = $mainContainer.find( '#idOfElement' ).is( ':visible' );
if ( isVisible ) {
cy.get( '#idOfElement' ).click();
this.clickVisibleButton();
}
} );
}
Then obviously call the this.clickVisibleButton() in your test. I'm using typescript and this method is setup in a class, but you could do this as a regular function as well.
I have the following code:
const until = protractor.ExpectedConditions;
export class GenericPOClass {
waitForPagetoLoad(numberOfSecondsToWait: number = 30) { // note the default
(1) let elementToWaitFor = element(by.css('app-spec-mapper .stepper'));
(2) browser.wait(until.presenceOf(elementToWaitFor), numberOfSecondsToWait*1000);
(3) return expect(elementToWaitFor.isPresent()).toBeTruthy('Element stepper is not actually present.');
}
}
This is code called from a protractor/jasmine test to wait for my non-angular web page to load.
If I use all 3 lines, with a "default wait time of 30 seconds, I make it past this loading part of my test and move onto another section.
If I use just lines 1 & 3, I get the expectation error.
If I use line 2 as well with a number of seconds to wait of 0, it works anyway. The page is loaded, line 3 passes.
Is there some type of default wait time on browser.wait that overrides my wait time? Is there some freaky magic of asynchronicity that it works if observed (browser.wait calling the expected conditions is magic)?
Coming from a synchronous Java and TestNG background, I've been learning protractor and JavaScript as I go, writing automated test cases for my job and things like this without a formal class or knowledgeable person to ask keep tripping me up. I've been wrangling with getting waiting for things to load working for a week, please help me understand this black magic.
Protractor Version: 5.1.2
NodeJS: 6.11.2
IDE: IntelliJ WebStorm
The black magic you speak of is a Promise. And your three seconds is wait UP TO three seconds, not wait EXACTLY three seconds.
First, let's confirm it really is a promise we're dealing with, then we'll see how a promise works.
Looking at the protractor source, you'll see that until.presenceOf()1 returns ElementFinder.isPresent:
presenceOf(elementFinder: ElementFinder): Function {
return elementFinder.isPresent.bind(elementFinder);
};
And ElementFinder.isPresent()2 returns a Promise containing a boolean
isPresent(): wdpromise.Promise<boolean> {
return this.count().then((count) => {
return count > 0;
});
}
Ok, it's promise, but what how does it work? Here's a simplified version of what's happening in your test:
var wait = 0
// Count to three then change content to 'Times up'
var timeout = setTimeout(timesUp, wait)
function timesUp() {
document.body.innerHTML = 'Times up'
}
// make a new Promise and keep a reference of its resolve function
var resolve
var p = new Promise(function(resolveFunc){
resolve = resolveFunc
})
// when the Promise is resolved by calling resolve(), cancel the timer
p.then(cancelTimeout)
function cancelTimeout() {
clearTimeout(timeout)
document.body.innerHTML = 'Timeout cancelled'
}
// resolve the promise (like presenceOf finding the element)
resolve()
In the snippet, setTimeout asks the runtime to run timesUp() zero seconds after the current execution finishes. But before the end of the execution, resolve() is called, so we see 'Timeout cancelled', not 'Times up'.
In your test, the same happens. Your element is found before a future round of execution can fail the test and the test passes.
Actually, the 'execution' I referred to is called the 'event loop': https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Event_loop
Hope that helps.
I'm working with Selenium on automating some testing for our company website but running in to issues.
TestItemFromSearch: (driver, part, qty) => {
Search.SearchItem(driver, part);
driver.findElement(By.id('enterQty')).findElement(By.tagName('input')).sendKeys(qty);
driver.findElement(By.linkText('Personalize')).click();
//This directs to a new page.
driver.wait(() => {
}, 1000).then(() => {
}, //TODO: This sucks. Fix it.
Login.Login(driver, Logins.username, Logins.password));
driver.findElement(By.className('selectDesignButton')).click();
}
Some times, when Selenium is directed to a new page, I ave to use the wait. No matter what condition I put in, it doesn't really find that condition - it fails. I have to hijack the Reject method to put in a desired action (like Login.Login).
I may have to do this again in the same test since it's going to run through multiple pages.. that's going to result in ugly, ugly, unreadable code.
How can I get Selenium to PROPERLY wait? If I do the following:
driver.wait(()=>{},1000)
it just sits forever. If I put in a return statement, it instantly fails and doesn't even wait the second.
You need to use an explicit (rather than implicit) wait. There are multiple ways to do this:
WebDriverWait wait = new WebDriverWait(driver, timeOut);
wait.until(ExpectedConditions.elementToBeClickable(locator));
Or you can use a different form of explicit wait, which is a fluentWait:
public WebElement fluentWait(final By locator) {
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
}); return foo; };;
Either will work, I personally prefer a fluentWait because you can specify:
Frequency with which FluentWait has to check the conditions defined.
Ignore specific types of exception waiting such as
NoSuchElementExceptions while searching for an element on the page.
Maximum amount of time to wait for a condition
Try this:
driver.wait(function() {
return driver.findElement(locator).isDisplayed();
}, timeout);
You should be using the selenium driver's WaitForElementPresent or WaitForElementVisible functionalities prior to interacting with elements on the page. The selenium driver will watch the DOM and verify the presence or rendering, respectively, of each element. After those checks pass first, it should be safe to interact with the page for further testing.
maybe this helps it is a "async function () "
var nameButton = By.linkText('Personalize')
await driver.wait(function () {
return until.elementIsVisible(nameButton);
}, 20000).then(() => {console.log("element visible")})
button = await button.findElement(nameButton).then(()=>{ console.log("found element")})
await button.click().then(()=>{ console.log("I clicked the button")})
I've been using selenium (with python bindings and through protractor mostly) for a rather long time and every time I needed to execute a javascript code, I've used execute_script() method. For example, for scrolling the page (python):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Or, for infinite scrolling inside an another element (protractor):
var div = element(by.css('div.table-scroll'));
var lastRow = element(by.css('table#myid tr:last-of-type'));
browser.executeScript("return arguments[0].offsetTop;", lastRow.getWebElement()).then(function (offset) {
browser.executeScript('arguments[0].scrollTop = arguments[1];', div.getWebElement(), offset).then(function() {
// assertions
});
});
Or, for getting a dictionary of all element attributes (python):
driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
But, WebDriver API also has execute_async_script() which I haven't personally used.
What use cases does it cover? When should I use execute_async_script() instead of the regular execute_script()?
The question is selenium-specific, but language-agnostic.
When should I use execute_async_script() instead of the regular execute_script()?
When it comes to checking conditions on the browser side, all checks you can perform with execute_async_script can be performed with execute_script. Even if what you are checking is asynchronous. I know because once upon a time there was a bug with execute_async_script that made my tests fail if the script returned results too quickly. As far as I can tell, the bug is gone now so I've been using execute_async_script but for months beforehand, I used execute_script for tasks where execute_async_script would have been more natural. For instance, performing a check that requires loading a module with RequireJS to perform the check:
driver.execute_script("""
// Reset in case it's been used already.
window.__selenium_test_check = undefined;
require(["foo"], function (foo) {
window.__selenium_test_check = foo.computeSomething();
});
""")
result = driver.wait(lambda driver:
driver.execute_script("return window.__selenium_test_check;"))
The require call is asynchronous. The problem with this though, besides leaking a variable into the global space, is that it multiplies the network requests. Each execute_script call is a network request. The wait method works by polling: it runs the test until the returned value is true. This means one network request per check that wait performs (in the code above).
When you test locally it is not a big deal. If you have to go through the network because you are having the browsers provisioned by a service like Sauce Labs (which I use, so I'm talking from experience), each network request slows down your test suite. So using execute_async_script not only allows writing a test that looks more natural (call a callback, as we normally do with asynchronous code, rather than leak into the global space) but it also helps the performance of your tests.
result = driver.execute_async_script("""
var done = arguments[0];
require(["foo"], function (foo) {
done(foo.computeSomething());
});
""")
The way I see it now is that if a test is going to hook into asynchronous code on the browser side to get a result, I use execute_async_script. If it is going to do something for which there is no asynchronous method available, I use execute_script.
Here's the reference to the two APIs (well it's Javadoc, but the functions are the same), and here's an excerpt from it that highlights the difference
[executeAsyncScript] Execute an asynchronous piece of JavaScript in
the context of the currently selected frame or window. Unlike
executing synchronous JavaScript, scripts executed with this method
must explicitly signal they are finished by invoking the provided
callback. This callback is always injected into the executed function
as the last argument.
Basically, execSync blocks further actions being performed by the selenium browser, while execAsync does not block and calls on a callback when it's done.
Since you've worked with protractor, I'll use that as example.
Protractor uses executeAsyncScript in both get and waitForAngular
In waitForAngular, protractor needs to wait until angular announces that all events settled. You can't use executeScript because that needs to return a value at the end (although I guess you can implement a busy loop that polls angular constantly until it's done). The way it works is that protractor provides a callback, which Angular calls once all events settled, and that requires executeAsyncScript. Code here
In get, protractor needs to poll the page until the global window.angular is set by Angular. One way to do it is driver.wait(function() {driver.executeScript('return window.angular')}, 5000), but that way protractor would pound at the browser every few ms. Instead, we do this (simplified):
functions.testForAngular = function(attempts, callback) {
var check = function(n) {
if (window.angular) {
callback('good');
} else if (n < 1) {
callback('timedout');
} else {
setTimeout(function() {check(n - 1);}, 1000);
}
};
check(attempts);
};
Again, that requires executeAsyncScript because we don't have a return value immediately. Code here
All in all, use executeAsyncScript when you care about a return value in a calling script, but that return value won't be available immediately. This is especially necessary if you can't poll for the result, but must get the result using a callback or promise (which you must translate to callback yourself).
Why there no such function in javascript that sets a timeout for its continuation, saves the necessary state (the scope object and the execution point), terminates the script and gives the control back to the browser? After the timeout expires the browser would load back the execution context and continues the script, and we would have a real non browser blocking sleep functionality that would work even if the JS engine is single threaded.
Why there is still no such functionality in javascript? Why do we have to still slice our code into functions and set the timeouts to the next step to achieve the sleep effect?
I think 'sleep'ing is something you do not want in your browser.
First of all it might be not clear what has to happen and how a browser should behave when you actually sleep.
Is the complete Script runtime sleeping? Normally it should because you only have one thread running your code. So what happens if other events oocur during sleep? they would block, and as soon execution continues all blocked events would fire. That will cause an odd behaviour as you might imagine (for instance mouse click events which are fired some time, maybe seconds, after the actual click). Or these events had to be ignored, which will lead to a loss of information.
What will happen to your browser? Shall it wait for sleep if the user clicks a (e.g. close window) button? I think not, but this might actually call javascript code again (unload) which will not be able to be called since program execution is sleeping.
On a second thought sleep is a sign of poor program design. Actually a program/function/you name it has a certain task, which shall be completed as soon as possible. Sometimes you have to wait for a result (for instance you wait for an XHR to complete) and you want to continue program execution meanwhile. In this case you can and should use asynchronous calls. This results in two advantages:
The speed of all scripts is enhanced (no blocking of other scripts due to sleep)
The code is executed exactly when it should and not before or after a certain event (which might lead to other problems like deadlocks if two functions check for the same condition ...)
... which leads to another problem: Imagine two or more pieces of code would call sleep. They would hinder themselves if they try to sleep at the same, maybe unnecessarily. This would cause a lot of trouble when you like to debug, maybe you even have difficulties in ensuring which function sleeps first, because you might control this behavior somehow.
Well I think that it is one of the good parts of Javascript, that sleep does not exist. However it might be interesting how multithreaded javascripts could perform in a browser ;)
javascript is desgined for single process single thread runtime, and browser also puts UI rendering in this thread, so if you sleep the thread, UI rendering such as gif animation and element's event will also be blocked, the browser will be in "not responding" state.
Maybe a combination of setTimeout and yield would work for your needs?
What's the yield keyword in JavaScript?
You could keep local function scope while letting the browser keep going about its work.
Of course that is only in Mozilla at the moment?
Because "sleep()" in JavaScript would make for a potentially horrible user experience, by freezing the web browser and make it unresponsive.
What you want is a combination of yield and Deferreds (from jquery for example).
It's called sometimes pseudoThreads, Light Threading or Green Threads. And you can do exactly what you want with them in javascript > 1.7 . And here is how:
You'll need first to include this code:
$$ = function (generator) {
var d = $.Deferred();
var iter;
var recall = function() {
try {var def = iter.send.apply(iter, arguments);} catch(e) {
if (e instanceof StopIteration) {d.resolve(); return;}
if (e instanceof ReturnValueException) {
d.resolve(e.retval); return
};
throw e;
};
$.when(def).then(recall); // close the loop !
};
return function(arguments) {
iter = generator.apply(generator, arguments);
var def = iter.next(); // init iterator
$.when(def).then(recall); // loop in all yields
return d.promise(); // return a deferred
}
}
ReturnValueException = function (r) {this.retval = r; return this; };
Return = function (retval) {throw new ReturnValueException(retval);};
And of course call jquery code to get the $ JQuery acces (for Deferreds).
Then you'll be able to define once for all a Sleep function:
function Sleep(time) {
var def = $.Deferred();
setTimeout(function() {def.resolve();}, time);
return def.promise();
}
And use it (along with other function that could take sometime):
// Sample function that take 3 seconds to execute
fakeAjaxCall = $$(function () {
yield (Sleep(3000));
Return("AJAX OK");
});
And there's a fully featured demo function:
function log(msg) {$('<div>'+msg+'</div>').appendTo($("#log")); }
demoFunction = $$(function (arg1, arg2) {
var args = [].splice.call(arguments,0);
log("Launched, arguments: " + args.join(", "));
log("before sleep for 3secs...");
yield (Sleep(3000));
log("after sleep for 3secs.");
log("before call of fake AjaxCall...");
ajaxAnswer = yield (fakeAjaxCall());
log("after call of fake AjaxCall, answer:" + ajaxAnswer);
// You cannot use return, You'll have to use this special return
// function to return a value
log("returning 'OK'.");
Return("OK");
log("should not see this.");
});
As you can see, syntax is a little bit different:
Remember:
any function that should have these features should be wrapped in $$(myFunc)
$$ will catch any yielded value from your function and resume it only when
the yielded value has finished to be calculted. If it's not a defered, it'll work
also.
Use 'Return' to return a value.
This will work only with Javascript 1.7 (which is supported in newer firefox version)
It sounds like what you're looking for here is a way to write asynchronous code in a way that looks synchronous. Well, by using Promises and asynchronous functions in the new ECMAscript 7 standard (an upcoming version of JavaScript), you actually can do that:
// First we define our "sleep" function...
function sleep(milliseconds) {
// Immediately return a promise that resolves after the
// specified number of milliseconds.
return new Promise(function(resolve, _) {
setTimeout(resolve, milliseconds);
});
}
// Now, we can use sleep inside functions declared as asynchronous
// in a way that looks like a synchronous sleep.
async function helloAfter(seconds) {
console.log("Sleeping " + seconds + " seconds.");
await sleep(seconds * 1000); // Note the use of await
console.log("Hello, world!");
}
helloAfter(1);
console.log("Script finished executing.");
Output:
Sleeping 1 seconds.
Script finished executing.
Hello, world!
(Try in Babel)
As you may have noticed from the output, this doesn't work quite the same way that sleep does in most languages. Rather than block execution until the sleep time expires, our sleep function immediately returns a Promise object which resolves after the specified number of seconds.
Our helloAfter function is also declared as async, which causes it to behave similarly. Rather than block until its body finishes executing, helloAfter returns a Promise immediately when it is called. This is why "Script finished executing." gets printed before "Hello, world!".
Declaring helloAfter as async also allows the use of the await syntax inside of it. This is where things get interesting. await sleep(seconds * 1000); causes the helloAfter function to wait for the Promise returned by sleep to be resolved before continuing. This is effectively what you were looking for: a seemingly synchronous sleep within the context of the asynchronous helloAfter function. Once the sleep resolves, helloAfter continues executing, printing "Hello, world!" and then resolving its own Promise.
For more information on async/await, check out the draft of the async functions standard for ES7.