I just started using the selenium, but after reading the docs I wasn't able to understand how to properly perform a wait (using EC) until webdriver identifies presence of an element which is a child of another element.
Let me explain. I want a specific element which I can access in 2 steps:
#find major group
listings = driver.find_element_by_id("new-listings");
#find subelement
checkbox = listings.find_element_by_class_name("listing-category")
That's OK, but I want to use EC to ensure that the checkbox is present. I cannot use smth like:
checkbox = driver.wait.until(EC.presence_of_element_located(
(By.CLASS_NAME, "listing-category")))
Just because there is a bunch of other similar listing-category elements. And sadly the only way to locate checkbox is through the nested request like illustrated earlier (it doesn't have any id or whatever).
How to properly express it?
Small extra question: in general, is it very bad for performance to use EC instead of just hitting elements and hope that they are already available? 99% of the time I will not need to wait for element to appear, so I just try to handle rare situation when browser is less responsive than usual. But I am not sure if EC introduces significant overhead for just creating the event handler etc.
I would suggest that you use a CSS selector to accomplish this in one go. You can use the method you are describing but with a CSS selector you can find the child of an element with a single locator. In this case the CSS selector would be #new-listings .listing-category. In CSS selectors, # indicates an ID and . indicates a class name. The space between the two parts indicates a descendant. If you wanted a child (a direct descendant), you would use >, e.g. #new-listings > .listing-category.
To your question about EC and performance, no it doesn't slow things down. It always checks immediately to see if the condition is true, if it's not then it waits so it generally doesn't hurt to add a wait.
If you are new to CSS selectors, here are some links that you can read up on and learn about them. They are very powerful and quick locators.
CSS selector reference
CSS selector tips
Advanced CSS selectors
Related
I'm searching for a tag in class.I tried many methods but I couldn't get the value.
see source code
The data I need is inside the "data-description".
How can i get the "data-description" ?
I Tried some method but didn't work
driver.find_element_by_name("data-description")
driver.find_element_by_css_selector("data-description")
I Solved this method:
icerisi = browser.find_elements_by_class_name('integratedService ')
for mycode in icerisi:
hizmetler.append(mycode.get_attribute("data-description"))
Thanks for your help.
I think css selector would work best here. "data-description" isn't an element, it's an attribute of an element. The css selector for an element with a given attribute would be:
[attribute]
Or, to be more specific, you could use:
[attribute="attribute value"]
Here's a good tip:
Most web browsers have a way of copying an elements Selector or XPATH. For example, in Safari if you view the source code then right-click on an element it will give you the option to copy it. Then select XPATH or Selector and in your code use driver.find_element_by_xpath() or driver.find_element_by_css_selector(). I am certain Google Chrome and Firefox have similar options.
This method is not always failsafe, as the XPATH can be very specific, meaning that slight changes to the website will cause your script to crash, but it is a quick and easy solution, and is especially useful if you don't plan on reusing your code months or years later.
I am trying to write a Selenium test but the issue is I have learned that the page is generated with PrimeFaces, thus the element IDs randomly change from time to time. Not using IDs is not very reliable. Is there anything I can do?
Not having meaningful stable IDs is not a problem, as there are always alternative ways to locate elements on a page. Just to name a few options:
partial id matches with XPath or CSS, e.g.:
# contains
driver.find_element_by_css_selector("span[id*=customer]")
driver.find_element_by_xpath("//span[contains(#id, 'customer')]")
# starts with
driver.find_element_by_css_selector("span[id^=customer]")
driver.find_element_by_xpath("//span[starts-with(#id, 'customer')]")
# ends with
driver.find_element_by_css_selector("span[id$=customer]")
classes which refer/tell some valuable information about the data types ("data-oriented locators"):
driver.find_element_by_css_selector(".price")
driver.find_element_by_class_name("price")
going sideways from a label:
# <label>Price</label><span id="65123safg12">10.00</span>
driver.find_element_by_xpath("//label[.='Price']/following-sibling::span")
links by link text or partial link text:
driver.find_element_by_link_text("Information")
driver.find_element_by_partial_link_text("more")
And, you can, of course, get creative and combine them. There are more:
Locating Elements
There is also this relevant thread which goes over best practices when choosing a method to locate an element on a page:
What makes a good selenium locator?
I'm trying to learn how to click around using selenium. I have tried some different websites like reddit, google etc without success.
driver.get('https://www.dropbox.com/login')
driver.find_element_by_xpath('//a[#href="' + 'https://www.dropbox.com/forgot?email_from_login=' + '"]').click()
and
continue_link = driver.find_element_by_partial_link_text('Sign in')
They both exist but neither works. What am I doing wrong?
You're being a little specific in your xpath, so there're many more ways you might bugger something up. Using the xpath you can simply do:
driver.find_element_by_xpath("//*[#class='forgot-password-link']").click()
I make no assumptions, but just in case you aren't already, there's a very handy tool in Chrome's inspect element which lets you click an element and jumps to its node in the inspector.
Can you try the following:
from selenium.webdriver.common.by import By
driver.findElement(By.cssSelector(".login-button.signin-button.button-primary")).click()
You can use a shorter single class selector to Sign In button
driver.find_element_by_css_selector(".login-button").click()
Sign in with Google
driver.find_element_by_css_selector(".sign-in-text").click()
For forgot password
driver.find_element_by_css_selector(".forgot-password-link").click()
Single class css selectors will be the fastest method (faster than xpath and compound class)
I am asking for generally checking if all elements of a page has been loaded. Is there a way to check that basically?
In the concrete example there is a page, I click on some button, and then I have to wait until I click on the 'next' button. However, this 'Next' button is available, selectable and clickable ALL THE TIME. So how to check with selenium that 'state'(?) of a page?
As a reminder: This is a question about selenium and not the quality of the webpage in question....
As your question is about if there is a way with python-selenium to wait until all elements of a page has loaded, the Answer is No.
An Alternate Approach
Fundamentally, you can write a line of code (or implement it through a function) to check for the 'document.readyState' == "complete" as follows :
self.driver.execute_script("return document.readyState").equals("complete"))
But this approach have a drawback that it doesn't accounts for the JavaScript / AJAX Calls to be completed.
Why No
Writing the above mentioned line to wait for Page Loading is implemented by default through Selenium. So rewriting the same is a complete overhead. The client (i.e. the Web Browser) will never return the control back to the WebDriver instance until and unless 'document.readyState' is equal to "complete". Once this condition is fulfilled Selenium performs the next line of code.
It's worth to mention that though the client (i.e. the Web Browser) can return back the control to the WebDriver instance once 'document.readyState' equal to "complete" is achieved, it doesn't guarantees whether all the WebElements on the new HTML DOM are present, visible, interactable and clickable.
So, as per our requirement, if the next *WebElement with which you have to interact is not interactable by the time 'document.readyState' equal to "complete" is achieved, you have to induce WebDriverWait inconjunction with a matching expected_conditions with respect to the individual WebElement. Here are a few of the most used expected_condition:
element_to_be_clickable(locator)
presence_of_element_located(locator)
visibility_of(element)
References
You can find a couple of relevant discussions in:
Do we have any generic function to check if page has completely loaded in Selenium
Selenium IE WebDriver only works while debugging
Selenium how to manage wait for page load?
Reliably determining whether a page has been fully loaded can be challenging. There is no way to know if all the elements have been loaded just like that. You must define some "anchor" points in each page so that as far as you aware, if these elements has been loaded, it is fair to assume the whole page has been loaded. Usually this involves a combination of tests. So for example, you can define that if the below combination of tests passes, the page is considered loaded:
JavaScript document.readyState === 'complete'
"Anchor" elements
All kinds of "spinners", if exist, disappeared.
There is something called the document.readyState which you can retrieve by executing a JavaScript script via Selenium. This doesn't work for dynamically loaded content. It returns one of these three states:
Loading
The document is still being loaded, no css or other resources are available
Interactive
The document has been loaded, no css or other resources are available
Complete
Both the document and the css / other resources are loaded
You're looking for at least Interactive. You can retrieve the state by calling execute_script:
driver.execute_script("return document.readyState")
Using selenium and python to create some GUI tests. I'm testing to make sure a button is present and blue ("active") before clicking on it...but sometimes the color is hex and sometimes it's rgb, so I need to check either one of those in my web driver wait.
Here's what I have:
xpath = str(locator) + "[contains(#style, 'background: rgb(223, 239, 252)')]"
WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, xpath)))
Now this works, but I want to add OR 'background: #2E6E9E' as a condition to move on from the wait.
I used this post to find out how to condition a wait on a css style.
Also, an example of what would be passed into locator would be
"//button[#id='button1']"
So xpath would be
"//button[#id='button1'][contains(#style, 'background: rgb(223, 239, 252)')]"
There are a few ways you can accomplish this. You can go down the path you are looking at now and use an OR with either a CSS Selector or an XPath. These are well documented.
Another option is create a custom wait. An example of this is documented in this answer. You could take advantage of element.value_of_css_property("background") which returns a string in an rgb format. Even if the format is specified in hex, it will return rgb so you won't need an OR in this case. You can also use the Selenium color support module documented here.