I'm learning GEB in IntelliJ and have two issues.
When I click button on the top of the page I'm redirected to very bottom of the page.
After this I need to do assertion that the site slided down.
I try to do assertion in this example:
assert page.element_on_the bottom.isDisplayed() == true
// element_on_the bottom {$('css_selector)
The above assertion always returns true even I don't click button to slide down.
I need to check if element is visible on the part of website which is actually displayed on my monitor screen. Is there a way to do this?
I try to use waitFor statement in example:
waitFor{page.element.isDisplayed()}
but it doesn't work:
geb.waiting.WaitTimeoutException: condition did not pass in 5.0 seconds (failed with exception)
instead of this I use:
Thread.sleep(3000) //which is not desirable here
and then my test passes. I think my element don't trigger any js or ajax script actions.
I'm not sure how to use waitFor that should wait for all elements to load.
Element doesn't have to be in view for is isDisplayed() to return true - it will return true as long as the element is visible on page, e.g. it's display property is not set to hidden. You will need to detect your scroll position using javascript because WebDriver does not expose scroll information. See this response for how to detect that scroll is at the bottom of the page and see this section of the Book of Geb for how to execute javascript code in the browser.
What is the exception and its stacktrace that you're getting from your waitFor {} call? It probably contains the clue on what is actually going on.
For your first problem, can you please try the following as displayed should work fine for the visibility and present should be good to check the presence of the css selector in the DOM:
waitFor { page.element_on_the bottom.isDisplayed() }
or
waitFor { page.element_on_the bottom.displayed() }
For the second problem, you need to edit your Gebconfig file, like below as the waiting time you have right now is 5 seconds that's why it's failing whereas your sleep time is way more than 5 seconds:
waiting {
timeout = 30
retryInterval = 0.1
}
or, you can also try that at the same line of the code as below:
waitFor (30, 0.1) {page.element.isDisplayed()}
Please let us know if that worked fine or not! On another note, why don't you simply write the function name from the imported class instead of always writing className.functionName()? Best of luck and Cheers!!
Related
I am working on a website and trying to test it with Selenium and jUnit. I'm getting race conditions between the test and the site, despite my best efforts.
The front end of the site is HTML and jQuery. The back end (via AJAX) is PHP.
The site
I have two required text input fields (year and age), plus some others that I'm not changing in the tests that give problems. As soon as both text inputs are non-empty, an AJAX call is made to the back end. This will return 0+ results. If 0 results are returned, a results div on the screen gets some text saying that there were no results. If >0 results are returned, a table is written to the results div showing the results.
I don't want the site to wait until e.g. 4 digits' worth of year is entered before doing the AJAX call as it could be looking at ancient history (yes, really). So, as soon as both are non-empty the call should be made. If you type slowly, this means that entering e.g. 2015 will trigger calls for year=2, year=20, year=201 and year=2015. (This is OK.)
The test
I'm using page objects - one for the inputs and one for the output. At the start of the test, I wait for a prompt to be present on the screen (please enter some data) as that is generated by JavaScript that checks the state of the input fields - so I know that the page has loaded and JavaScript has run.
The wait for a prompt is made immediately after the page object is created for the output. This is the relevant method in the page object:
// Wait until the prompt / help text is displayed. Assumes that the prompt text always contains the word "Please"
public void waitForText() {
wait.until(ExpectedConditions.textToBePresentInElementLocated(By.id("resultContainer"), "Please"));
}
The method for setting the year is
public void setYear(String year){
WebElement yearField = driver.findElement(By.id(yearInputId));
if (yearField == null) {
// This should never happen
Assert.fail("Can't find year input field using id " + yearInputId);
} else {
yearField.sendKeys(new String [] {year});
driver.findElement(By.id(ageInputId)).click(); // click somewhere else
}
}
and there's a corresponding one for age.
I have a series of methods that wait for things to happen, which don't seem to have prevented the problem (below). These do things like wait for the current result values to be different from a previous snapshot of them, wait for a certain number of results to be returned etc.
I create a driver for Chrome as follows:
import org.openqa.selenium.chrome.ChromeDriver;
// ...
case CHROME: {
System.setProperty("webdriver.chrome.driver", "C:\\path\\chromedriver.exe");
result = new ChromeDriver();
break;
}
The problem
Some of the time, things work OK. Some of the time, both inputs are filled in with sensible values by the test, but the "there are 0 results" message is displayed. Some of the time, the test hangs part-way through filling in the inputs. It seems to be fine when I'm testing with Firefox, but Chrome often fails.
The fact that there is unpredictable behaviour suggests that I'm not controlling all the things I need to (and / or my attempts to control things are wrong). I can't see that I'm doing anything particularly weird, so someone must have hit these kinds of issue before.
Is there a browser issue I'm not addressing?
Is there something I'm doing wrong in setting the values?
Is there something I'm doing wrong in my test choreography?
It could be that when you start typing, the script is still loading or that there's a pending Ajax call when you start handling the next field or validation.
You could try to synchronize the calls with a low level script :
const String JS_WAIT_NO_AJAX =
"var callback = arguments[0]; (function fn(){ " +
" if(window.$ && window.$.active == 0) " +
" return callback(); " +
" setTimeout(fn, 60); " +
"})();";
JavascriptExecutor js = (JavascriptExecutor)driver;
driver.manage().timeouts().setScriptTimeout(20, TimeUnit.SECONDS);
js.executeAsyncScript(JS_WAIT_NO_AJAX);
driver.findElement(By.Id("...")).sendKeys("...");
js.executeAsyncScript(JS_WAIT_NO_AJAX);
driver.findElement(By.Id("...")).click();
I need to wait for a modal to close so I am waiting for the absence of an element. I'm trying to do something like this:
browser.driver.wait until.stalenessOf(By.css '.modal-header')
Unfortunately Coffeescript has reserved until. So I tried using:
browser.driver.wait browser.driver.until.stalenessOf(By.css '.modal-header')
but it is undefined (until that is). browser.driver exists but it doesn't contain until.
How can I access the until object? Also, is there another way to wait until an element is no longer on the page?
You can use the isPresent() function instead:
browser.wait ->
not element(By.css '.modal-header').isPresent()
,
5000
NOTE: isPresent() will not return false until it has finished implicitly waiting for the element, so if you have set the browser.manage().timeouts().implicitlyWait property very high you may want to temporarily decrease its value.
I've got an application which I want to test with selenium/codeception. It has a lot of ajax function which changes the pages (show/hide sections of the page) which codeception at the moment doesn't handle well.
The problem I am having is I want to click on buttons/elements which either
are already on the page (ajax calls finished early)
which are not yet on the page (waiting for ajax response)
If I use waitForElement() it only seems to works in the second case (it waits for the element to appear and continues) but if the element is already present waitForElement will timeout and fire an Fail.
What I am looking for is a seeOrWaitForElement() function but I can't figure out how to insert logic in codeception.
Is this function available somewhere or how can I solve this problem in another way?
You could use
waitForJs("return document.querySelector('".$element."') != null", $seconds);
which will wait until the element exists (and returns instantly if the element already is present).
Codeception introduced a feature called SmartWait in version 2.3.4 which seems like an elegant solution to your problem. Note that it does not work for all kind of locators.
From the Codeception documentation:
SmartWait
since 2.3.4 version
It is possible to wait for elements pragmatically. If a test uses element which is not on a page yet, Codeception will wait for few extra seconds before failing. This feature is based on Implicit Wait of Selenium. Codeception enables implicit wait only when searching for a specific element and disables in all other cases. Thus, the performance of a test is not affected.
SmartWait can be enabled by setting wait option in WebDriver config. It expects the number of seconds to wait. Example:
wait: 5
With this config we have the following test:
<?php
// we use wait: 5 instead of
// $I->waitForElement(['css' => '#click-me'], 5);
// to wait for element on page
$I->click(['css' => '#click-me']);
It is important to understand that SmartWait works only with a specific locators:
#locator - CSS ID locator, works
//locator - general XPath locator, works
['css' => 'button''] - strict locator, works
But it won’t be executed for all other locator types. See the example:
<?php
$I->click('Login'); // DISABLED, not a specific locator
$I->fillField('user', 'davert'); // DISABLED, not a specific locator
$I->fillField(['name' => 'password'], '123456'); // ENABLED, strict locator
$I->click('#login'); // ENABLED, locator is CSS ID
$I->see('Hello, Davert'); // DISABLED, Not a locator
$I->seeElement('#userbar'); // ENABLED
$I->dontSeeElement('#login'); // DISABLED, can't wait for element to hide
$I->seeNumberOfElements(['css' => 'button.link'], 5); // DISABLED, can wait only for one element
I used waitForJS from the docs and it worked. Example:
$I->see('Jane Doe', '#table tbody tr');
$I->click(['css' => '#reload-ajax']); // Ajax trigger here
$I->waitForJS("return $.active == 0;", 60);
$I->see('John Doe', '#table tbody tr');
What is the best way to wait for an element to appear on a web page? I have read that we can use implicit wait and functions like webdriverwait, fluentwait etc and last but not the least thread.sleep()...which i use the most but want to stop using at all.
My scenario:
User logs in to a website...website checks the credentials and provides an offer to the user in the form of an overlay (kind of popup but not a separate window). I need to verify text on the the overlay.
There is a time gap between user signing in and the overlay getting displayed. what is the best approach so that selenium waits only till the time the element is not visible. As the overlay is not a separate page but part of the main page, implicit wait does not work at all.
All suggestions are welcome...:)
WebDriverWait wait = new WebDriverWait(driver, 20);
wait.until(ExpectedConditions.elementToBeClickable(By.id("optionsBuilderSelect_input")));
I'm a professional scraper (http://nitinsurana.com) I've written 30+ softwares using selenium and I've never faced any such issue, anyways above is a sample code.
All I can think of is that what until condition has to be checked because many a times elements are already visible, but they are not clickable and things like that. I guess you should give different options a try and I hope you'll find the one required.
Always start by using a implicit wait. I think Selenium defaults to 5 seconds and so if you do a driver.findElement(), the implication is that it will wait up to 5 seconds. That should do it. If you are experiencing a scenario where the time it takes is unpredictable, then use FluentWait (with the same 5 second timeout) but also using the .ignoring method and wrap that inside a while loop . Here is the basic idea:
int tries=0;
while ( tries < 3 ) {
//fluent wait (with .ignoring) inside here
tries ++1;
}
public boolean waitForElement(WebElement ele, String xpath, int seconds) throws InterruptedException{
//returns true if the xpath appears in the webElement within the time
//false when timed out
int t=0;
while(t<seconds*10){
if(ele.findElements(By.xpath(xpath)).size()>0)
return true;
else{
Thread.sleep(100);
t++;
continue;
}
}
System.out.println("waited for "+seconds+"seconds. But couldn't find "+xpath+ " in the element specified");
return false;
}
You could wait for the presence of the element to appear as follows:
new WebDriverWait(driver, 10).until(ExpectedConditions.presenceOfElementLocated(By.id("someId")));
I am using Selenium to get div value, but the fallowing code is not waiting for the page, just for URL. I used time.sleep, which is very primitive and totally not flexible. I want to change it on the explicit, but I am not too experienced in Python and I have a problem with that.
The website name has been changed just in case :
def repeat():
import wx
while True:
botloc = driver.find_element_by_id('botloc').text
print botloc
botX,botY = map(int,botloc.split(','))
print botX
print botY
wx.Yield()
def checker():
if driver.current_url == 'logged.example.com':
time.sleep(5)
repeat()
else:
checker()
checker()
How can I replace time.sleep with something flexible to wait the shortest time as possible after the page will be loaded? How to use explicit correctly with my code?
I know that's possible with using an element from the website, but I can't write anything sensible, I just need an example.
Is possibility to use element_by_id('botloc') for wait till it will be visible then start repeat() ?
How can i replace time.sleep with something flexible to wait shortest
time as possible after the page will be loaded?
I suppose you use get(url) to load the page. Generally you don't have to do anything, WebDriver automatically waits until page is being loaded. So you can remove time.sleep(). However there are some issues reported when loading the page using get with firefox driver, because of that you will have to wait for some target element which is supposed to be in the loaded page as mentioned below.
How to use explicit correctly with my code?
Have you checked Selenium webdriver documentation ? you can wait for botloc element explicitly as below
//assuming you have a valid webdriver reference
//Ex: DEFAULT_WAIT = 10 means
//waits up to 10 seconds before throwing a TimeoutException or if it finds the element will return it in 0 - 10 seconds.
element = WebDriverWait(webdriver, DEFAULT_WAIT).until(EC.presence_of_element_located((By.ID, "botloc")))
Refer this page for more information