There are waiting and non-waiting functions in Capybara. I am trying to use the first as much as possible.
Is there a way to use find (or one of its derivatives) to look for a CSS element that appears asynchronously?
Unfortunately, function page.has_css? is a non-waiting function.
Your claim that page.has_css? is a non-waiting function is not correct. page.has_css? will wait up to Capybara.default_max_wait_time seconds for an element to appear on the page and return true or false based on whether or not the element is on the page within that time. Therefore, given Capybara.default_max_wait_time = 5
if page.has_css?("div#my_div")
puts "on page"
else
puts "not on page"
end
will wait for up to 5 seconds. As soon as a div with id of "my_div" is visible on the page it will print "on page". If a matching div doesn't become visible within 5 seconds it will print "not on page". If you want it to wait longer for a specific call you can override default_max_wait_time by passing the :wait option
if page.has_css?("div#my_div", wait: 10) # will wait up to 10 seconds
...
If rather than wanting a boolean response you want to ensure the element exists you can do any of
page.assert_selector(:css, 'div#my_div') # :css is the default so can usually be omitted
page.assert_css('div#my_div') # minitest
expect(page).to have_css('div#my_div') # RSpec
which will wait for the element to appear as previously, but raise an exception rather than returning false if the element does not appear
If you need the actual element you can use find which will also wait. So
el = page.find(:css, 'div#my_div') # :css is the default and can usually be omitted
will wait for the element to appear and return the element when found. If the element is not found within default_max_wait_time seconds it will raise an exception.
Note: Basically, the only non-waiting methods are all and first and even those can be made to wait if you pass in any of the count options (:count, :minimum, :maximum, :between), although the elements returned from them would still be non-reloadable and can therefore have further behavior effects if used for scoping, etc. Even methods like click_link, click_button, etc will wait since they are implemented as find(...).click, etc
Related
In one of my testcases, im checking if some elements are NOT visible.
Its very simple:
Login
Check if 5 buttons inside the left menu bar are not visible (As the user has no rights)
End test
This shouldnt take more than 10 seconds to test.
But as im using implicit wait, it does always wait 5 seconds for each button, which causes the test to take way too much time to finish. Currently it takes more than 30 seconds just to test if 5 buttons are not visible.
Im using this method:
var elements = _webDriver.FindElements(By.XPath(selector));
if (elements.Count > 0)
{
throw new Exception(selector + " still displayed");
}
Are there any other ways how to make this work?
Use a explicit wait to check for the first element and then just check the other elements directly (with implicitly_wait = 0).
something like:
try:
element = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.XPATH, selector))
)
except TimeoutException:
# no element found
if element:
# check the presence of the other elements with find_element
documentation:
https://selenium-python.readthedocs.io/waits.html
These two lines don't work as expected:
wait until element is visible ${my xpath}
SeleniumLibrary.click element ${my xpath}
Approximately half of the time the second line fails. I am not in front of my computer now but it either fails with stale element or couldn't find element with id....
If I add a sleep 3 or so inbetween the lines the second line never fails.
These two lines work frequently but they also fail frequently. Is there a difference in how RF detects that something is visible and how Selenium sends a click to the same element?
You can include a custom timeout in the first keyword instead of using sleep.
Wait Until Element Is Visible ${my xpath} 3 seconds
Click Element ${my xpath}
To answer your question in short no there is no difference between how RF detects that something is visible and how selenium sends a click to that same element. I looked at the source code for Selenium and both functions use the same locator argument.
#keyword
def click_element(self, locator, modifier=False, action_chain=False):
and
#keyword
def wait_until_element_is_visible(self, locator, timeout=None, error=None):
A keyword instead I would suggest is
wait until page contains element ${my xpath}
This allows for the web page to be fully loaded before looking for the element.
I currently have a test script using WebdriverIO that clicks on a button and waits for a popup to display. The purpose of the test is to make sure the element is displayed after a click, but since it is asynchronous I must wait till it is displayed.
But if I wait until the element is displayed my assertion will always pass if waitForVisible is successful. If it is not successful it will always throw an exception on the waitForVisible command and the assertion will never execute.
Here is an example of the code:
browser.click(btnElement)
browser.waitForVisible(popupElement)
expect(browser.isVisible(popupElement)).to.be.true
Is there a different way to tell the expect statement to retry/wait until the element is visible so the assertion is not useless?
You seem to be confusing the purpose of assertions, or misusing them, specifically in your example. You should only use assertions when you have the certainty that the thing (WebElement, text, attribute of an element, etc.) you want to verify contains the expected type of value (which you return into your assert, or expect statement for the final validation => the output is the desired one).
Assertions validate a static value, they don't poll the DOM. That's why you have tools like .waitForVisible(), waitForText(), or more importantly .waitUntil() (which offers more flexibility).
Granted, the expect assertion-style provided by ChaiJS might have a confusing lexicon (expect could be taken out of context: it is expecting of the WebElement to be visible). Just use them for validating outputs of different commands, not states of WebElements, or other dynamic/changing elements.
That being said, as a best-practice, you should always wrap your commands (e.g.: .click()) into a .waitUntil() block and assure the WebElement is ready for the action you're about to perform:
is it rendered in the DOM by the front-end logic? (use .isExisting())
is it visible in the DOM? (you *cannot click on elements which aren't visible in the viewport)
is it clickable? (or w/e other action you want to perform on it...)
In the end, a idiomatic action (in our case .click()) should look like this:
browser.waitUntil(() => {
browser.isExisting(locator);
}, timeout, "Oups! An error occured.\nReason: element ('" + locator + "') does not exist");
browser.waitUntil(() => {
browser.isVisible(locator);
}, timeout, "Oups! An error occured.\nReason: element ('" + locator + "') is not visible");
browser.waitUntil(() => {
browser.click(locator);
}, timeout, "Oups! An error occured.\nReason: element ('" + locator + "') could not be clicked");
You can wrap the whole thing into a custom-command and use it w/e you want. Say good-bye to flaky tests! :)
* Selenium is a user-centric web-automation tool, thus all actions are performed as if a user would (a user cannot click on elements that aren't visible, a user cannot click on multiple elements at the same time, or fill in an input field by typing a whole word, or a whole paragraph, etc.)
I don't know WebdriverIO but I would assume that it has something akin to a try-catch. Wrap your wait in a try-catch. If it succeeds, Assert.pass() or the equivalent. If it times out, an exception will be thrown so in your catch for the timeout put an Assert.fail() or the equivalent.
I've never used ChaiJS but I did take a look at the documentation and found a couple things you should be able to use.
assert.isOk(false, 'this will fail');
This is from the documentation and should be the equivalent to what I'm familiar with, Assert.Fail(). One would assume that a slight adjustment the following would be the equivalent to Assert.Pass(),
assert.isOk(true, 'this will pass?');
If that doesn't work for some reason, you can use the below from the documentation.
assert.isNotOk(false, 'this will pass');
in my page object I have simple method
def clickSomething(byName) {
$("a.name", text: contains(byName)).click()
}
and during execution it does not find required element and goes further.
it happens because, according to documentation, $() returns EmptyNavigator if element not found.
I want for test to fail with some kind of "ElementNotFoundException" or "NullPointerException" on trying to make click on null element.
I also do not want to add additional checks for returned element (because I would need to add that for every element identification).
Is there an elegant workaround for that ?
e.g. for elements declared within "content" there is performed such a check. But what is the best practice for elements found outside content block ?
The issue that you've encountered which is click() not throwing an error when called on en empty navigator has been fixed recently and will be released in the next version of Geb.
If you need to get an error when a selector results in an empty navigator then you can either:
wrap your selector in a content definition with the required template option set to true which is the default
call verifyNotEmpty() on your navigator
I have a div with a unique ID. Under that div are a bunch of span elements that have className=foo. There are several span elements with className=foo but they are unique to each div (if that's clear). So my Selenium code first gets the unique div as a web element then tries to take that element and get by class name the span like so
element = sDriver.findElement(By.id("c_"+cID));
String sTest = element.findElement(By.className("actions")).getText();
On the second line it throws an exception every time
org.openqa.selenium.StaleElementReferenceException: Element not found in the cache - perhaps the page has changed since it was looked up
Command duration or timeout: 22 milliseconds
Do I misunderstand how to get that span from under a unique div?
Nope you'right accessing the span but the problem is that the Dom has changed since StaleReferenceException is about (see StaleReferenceException )
This may be caused because the page isn't loaded completely when the code starts or changes when the code is executed. You can either try to wait a little longer for the element or catch the StaleReferenceException and try again finding the div and the span.
My solution is not fancy but it works like a Swiss watch (in my situation of course). So my code is calling the parent element in a loop looking for different child elements in it. Nothing got changed at all - just simple querying and the exception began to occur. So! I've added the Thread.Sleep(2000) command before every search of parent element and it solver the problem. Not elegant but works every time with minimum code to debug afterwards.