any waitForJs function to wait for some javascript code returns true - selenium

This is question about golang selenium webdriver.
Is there any function that returns only after some js code return true.
var session *webdriver.Session
...
session.waitForJs(`$('#redButton').css('color')=='red'`)
// next code should be executed only after `#redButton` becomes red
The problem is that method session.waitForJs do not exist.

I don't see any wait functions in the golang bindings to Selenium, so you'll most likely need to define your own. This is my first attempt at golang, so bear with me:
type elementCondition func(e WebElement) bool
// Function returns once timeout has expired or the element condition is true
func (e WebElement) WaitForCondition(fn elementCondition, int timeOut) {
// Loop if the element condition is not true
for i:= 0; !elementCondition(e) && i < timeOut; i++ {
time.sleep(1000)
}
}
There are two options to define the elementCondition. Your method of using Javascript looks like it could work with the ExecuteScript function documented in webdriver.go
// Inject a snippet of JavaScript into the page for execution in the
context of the currently selected frame. The executed script is
assumed to be synchronous and the result of evaluating the script is
returned to the client.
An alternative is to access the element properties through Selenium
func ButtonIsRed(WebElement e) (bool) {
return (e.GetCssProperty('color') == 'red')
}
So your code would become
var session *webdriver.Session
....
// Locate the button with a css selector
var webElement := session.FindElement(CSS_Selector, '#redButton')
// Wait for the button to be red
webElement.WaitForCondition(ButtonIsRed, 10)

Related

WebDriverWait times out when run through Continuous Integration

I use the following code to check if an element is visible for my automated tests before interacting with them (Selenium/C#).
public bool ElementVisible(IWebDriver driver, IWebElement element, int secondsToWait = 30, bool doNotFailTest = false)
{
try
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(secondsToWait));
wait.Until<IWebElement>(d =>
{
if (element.GetCssValue("display") != "none" && element.Size.Height > 0 && element.Size.Width > 0)
{
return element;
}
return null;
});
return true;
}
catch (Exception)
{
if (!doNotFailTest)
{
throw;
}
return false;
}
}
The tests that use this method work every time, when I run tests on my PC. However, when I trigger the tests to be run on our build machine from TFS's Continuous Integration, only then does this method time out when called by my tests. Another point that might be worth noting is: this method works on other websites we test (both locally and through the CI). Just not this one website for some reason...
I have tried:
Running the tests locally on the build machine, cutting out the CI = no issue.
Increased the time out several times to greater values = method times out at the greater values.
Added Thread.Sleep wait before the above try/catch block (my reasoning for this stems from an issue I found on Browserstack whereby an AJAX injected element would not be found by this method alone without first adding an arbitrary wait before the WebDriverWait... which doesn't make sense to me because WebDriverWait is apparently the only thing I need to use to find AJAX injected elements (from what I've read anyway)).
As an aside, the reason I've made this method a bool is because not all of my tests should fail if certain elements aren't found. For example, our website rarely has the terms and conditions updated. When it does, a modal is presented to the user when logging in. We've left this as a manual test, but to prevent this modal from breaking the daily runs, we look for it and accept the terms if it's there (suppressing exceptions).
This timeout error relates to an element of the test we do actually want to find, it is the condition to pass our LogIn test by finding an element on our website that is only present when logged in. This element is injected with AJAX.
Better colleagues than me at CI can't pinpoint why this issue is occurring. Theoretically, the trigger from the CI is simply to initiate the test run - it has no other involvement in the running of the tests...?
I have observed that when triggering the test run from the CI to the build machine, when I VPN to the build machine, I expect to see the browser load up and the tests being conducted, but this is not the case. Maybe this is a factor? Perhaps I'm wrong but this behaviour seems like the tests are running on a headless browser? Yet we have not specified any settings to use a headless version of Chrome (v75).
If it was the case that testing through a headless browser throws timeout errors related to AJAX elements, other tests have passed after logging in - using other AJAX injected elements. It is only when any test calls this method, does it time out, when the run is triggered from the CI, on this specific website.
Very confusing!
I created a JavaScript version of the WebDriverWait, added some debugging (Console.Error.WriteLine()) to output the element's computed styles. The display property was 'none'... however, when I log in manually, in Chrome's developer tools, it is 'block'. Weird.
The element I was looking for was a DIV container that has the class 'loggedin'. I instead passed through an element inside this container that would also only be displayed when logged in (a piece of text). This worked.
My WebDriverWait doesn't work with either of the elements, so I have to stick with this JavaScript alternative. One thing to note, however, is that the evaluation of the element's properties takes 5 seconds, so I've omitted setting an interval between checks.
public bool ElementVisible(IWebDriver driver, By locator, int numberOfPolls = 10, bool doNotFailTest = false)
{
bool elementVisible = false;
int pollCount = numberOfPolls;
Thread.Sleep(1000);
while (!elementVisible && pollCount > 0)
{
try
{
// Takes 5 seconds to compute, bear this in mind when setting an interval or increasing the pollCount
elementVisible = (bool)js.ExecuteScript("var display = window.getComputedStyle(arguments[0]).display; var height = window.getComputedStyle(arguments[0]).height; var width = window.getComputedStyle(arguments[0]).width; return (display != 'none' && height != 0 && width != 0);", driver.FindElement(locator));
}
catch (Exception)
{
}
if (elementVisible)
{
break;
}
pollCount--;
}
if (!elementVisible)
{
if (!doNotFailTest)
{
throw new Exception("ElementVisible(): - element not visible.");
}
}
return elementVisible;
}

Locate all xpath attributes that contains part of the value

I need to wait for all elements to not be visible with an attribute 'id' that contains the word 'mask'. I have tried like this:
//*[contains(#id *= 'mask')]
However it is giving me an element not found exception.
I believe the syntax you're looking for is:
//*[contains(#id,'mask')]
I do something similar where I wait until the count is zero for classes that contain "BlockUI":
public static void waitForBlockUIToDisappear() {
// This function checks the entire currently-loaded web page for the
// presence of any web element of the
// class "blockUI" and loops until there are none. The presence of an
// element with this class does exactly
// what it implies: it blocks user input. If you get the error that a
// different web element would receive
// the click, for example, this is why and you'd need to call this
// method before doing a click. Generally,
// this function should be implemented before sending any input to a web
// page - click, sendkeys, select...
String blockUI = "//*[contains(#class,'blockUI')]";
while (true) {
if (driver.findElements(By.xpath(blockUI)).size() == 0)
break;
}
;
}
You should be able to alter that code to look for your id to contain your text.
You can use explicit wait in this case, as below.
WebDriverWait wait = new WebDriverWait(driver,15);
wait.until(ExpectedConditions.invisibilityOfAllElements(driver.findElements(By.xpath("//*[starts-with(#id,'mask')]"))));
In c# they were not implemented this method, but you can implement that.
Below is the url that can help you to write.
https://github.com/SeleniumHQ/selenium/blob/master/dotnet/src/support/UI/ExpectedConditions.cs#L140

Not waiting so long for an element

I have a table with several rows. Some of these rows may have a specific element and others may not. For sure some will and some won't.
I find the row and have it into a WebElement. Now to see whether an element is there I do the following (assume xp = ".//someelement)
List<WebElement> eles = row.findElements(By.xpath(xp));
if (eles.size() == 0) {
// element is not there
} else {
// element is there
}
This is fine when the element is present. When it is not, it takes like 30 seconds or a minute to figure out that it is not there. If called often this can really slow down the test. I can do
try {
WebElement ele = row.findElement(by.xpath(xp));
} catch (Exception ex) {
// element is not there
}
using a more detailed Exception. This works fine too but same problem. It waits a minute or half a minute.
Is there a way to check more quickly whether an element is present or not? If it were relative to driver (driver.findElementBy()) instead of an element (row.findElementBy()) I think I might know how to do it.
This is Java.
In your first example where you have a List of Elements you are not trying to locate one element; but several (let's say a collection of rows instead of one row). The second element ele is finding (or trying to find) a specific item (let's say 1 row). Hence, ideally you should say in your comments that some elementS were not there for eles . Nevertheless, the time issue is probably down to an implicit or explicit wait. Read here for more.
I prefer the first way where you check for a collection of elements (so you can aim it at a xpath and find all the tags included (or none at all). Ideally though you should go for an explicit wait.
Here is the method for waiting, it will return true/or false based on if the element was present during the polling time (10sec for example); worth noting that if the element is found as present earlier than the 10sec limit the loop will break and will return true. Of course you can play with timeOut to achieve the desired result; don't go to fast though (like 2 sec) otherwise you are risking your test occasionally failing because the tables were not loaded yet:
public boolean waitForElement(String elementXpath, int timeOut) {
try{
WebDriverWait wait = new WebDriverWait(driver, timeOut);
boolean elementPresent=wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(elementXpath)).isDisplayed());
System.out.printf("%nElement is present [T/F]..? ")+elementPresent;
}
catch(TimeoutException e1){e1.printStackTrace();elementPresent=false;}
return elementPresent;
}
I'm guessing that you are already using an explicit wait of 30sec for all of your findElement attempts hence the discrepancy.
Hope the above helps,
Best of luck!
Another option is to use WebDriverWait (explicit waits) rather than implicit ones.
This basically makes it so your tests will only wait a long time when you tell them too, otherwise they'll instantly fail if they don't find the elements you're looking for.
Adapted from slide52 of tourdedave's slideshare
// Use this class whenever you have to access the driver
// And you should only have to setDriver in a BeforeMethod when setting up.
// This method shows how to do it with a single browser
// This could be converted quite easily to threadlocals for parallel test runs
public class DriverManager {
private final WebDriver driver;
public static WebDriver getDriver() {
return driver;
}
public static setDriver(WebDriver driver) {
DriverManager.driver = driver;
}
public class WaitUntil {
public static Boolean displayed(By locator, Integer... timeout) {
try {
waitFor(ExpectedConditions.visibilityOfElementLocated(locator),
(timeout.length = 0 : null ? timeout[0];
} catch (TimeoutException exception) {
return false;
}
return true;
}
// add additional methods you want to wait for
// Look at the source of the ExpectedConditions class to see how to create
// your own
// e.g. presence of element
// number of results from locator changes
// element done moving
// url changes, etc.
private static void waitFor(ExpectedCondition<WebElement> condition, Integer timeout) {
timeout = timeout != null ? timeout[0] : 5; //default timeout if no value given
WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(condition);
}
}
Then in any class you can
By submitButtonBy = By.cssSelector(".submit);
use WaitUntil.displayed(submitButtonBy);
And it will wait for 5 seconds. If you want to wait for 10:
use WaitUntil.displayed(submitButtonBy, 10);
The nice thing about making a class with a bunch of methods like this is it's easy to add additional exceptions so you can choose to return false if there's a stale element or something else, rather than have to deal with a try catch in page classes or test classes.

Best way to find out if jqGrid is loaded and ready with Selenium

How are you finding out if jqGrid is loaded and ready to be used, via selenium.
Some details :
Im using C# driver
I have a method : new WebDriverWait(driver, new TimeSpan(0, 0, 0, 30)).Until(x => loadingdissapearedcondition) which im using to wait until Loading.. element is gone.
I also sometimes use this script :
private const string script = #"return ($('#{0}').jqGrid('getGridParam', 'reccount') !=x undefined) && ($('#{0}').jqGrid('getGridParam', 'reccount') != 0) && (!$('#load_{0}').is(':visible')) && (!$('#busyIcon').is(':visible'))";
private readonly string waitScript;
waitScript = string.Format(script, jqGridId);
public void WaitUntilLoadIconDissappears()
{
driver.WaitUntil(MAXWAIT, Wait);
}
public bool Wait()
{
var executeScript = ((IJavaScriptExecutor) driver).ExecuteScript(waitScript);
bool result;
bool tryParse = bool.TryParse(executeScript.SafeToString(), out result);
return tryParse && result;
}
to find if jqGrid has records and loading done.
I require something better - as even the above two does not make driver wait until load finishes, if we are using local data for jqGrid. Im also curious what is the best way, or at the minimum, how others are dealing with this problem.
I never used Selenium before, so I'm not sure that I understood your problem correctly. jqGrid will be first initialized and then (optionally) the data can be loaded from the server. During the initializing stage the original <table id="grid"></table> element will be converted to relatively complex HTML fragment which is the grid. At the end of the initialization the DOM element of the table (the $("#grid")[0]) will get the expando grid.
So you can use the test like
if ($("#grid")[0].grid) {
// grid is initialized
}
to determine that the grid is already initialized. jqGrid uses the same test internally (see here for example).
Here is solution for Java and jqgrid.
If grid data is not loaded yet then right pager has no value, so simply check its length. Methods such as isElementPresent, isDisplayed etc. seems not to work for grid right pager object. It's always present in page code while ajax, but text value is set when dynamic data is loaded.
public void waitForGridPagerRight(String gridName) throws Exception {
for (int second = 0;; second++) {
if (second >= 15) {
fail("Timeout.");
}
try {
if (driver
.findElement(By.id("pager_" + gridName + "_right"))
.getText().length() > 2)
break;
} catch (Exception e) {
}
Thread.sleep(1000);
}
}
Not sure why #Oleg's answer didn't work for me. It seemed like grid was being populated even before the ajax call was being made. It appears there's some change in newer versions maybe. It look like the last thing to happen in the ajax call is that the "loading" block is hidden, so instead I find that element and check it's display attribute a la:
def wait_for_jqgrid(self, t=20):
def check_jqgrid(driver):
#the last thing jqgrid does after the ajax call is to turn off the loading div
#so we find it and check to see if its display attribute is no longer set to 'none'
script = 'pid = $.jgrid.jqID($(".ui-jqgrid-btable").get(0).p.id); return $("#load_"+pid).get(0).style.display == "none";'
return driver.execute_script(script)
WebDriverWait(self.driver, t).until(check_jqgrid)
When the loading div is hidden, we are done. This is python but the same javascript bit should work in Java or whatever.

Selenium 2.20.0 waitForText - No Go

J Unit will not recognize waitForText method.
selenium.waitForText("some random text"); // no go
Any help will be appreciated.
selenium.click("link=some_link"); // works fine
I am trying for that particular piece of text to load from the next page before Selenium performs it's further actions.
I use a loop like so:
for (int second = 0;; second++) {
if (second >= maxWaitTime) fail("timeout");
try { if ("Some random text".equals(driver.findElement(By.cssSelector("xyz")).getText())) break; }
catch (Exception e) {}
Thread.sleep(1000);
}
I didn't know that selenium supports these types of wait methods. I typically create my own implementation in cases where I have to wait for some asynch event (typically Ajax)
You can basically do it with a loop that executes for as long as IsTextPresent is false.
I recommend adding a timeout in case of an error condition :-)