I have to test an e-shop. The users can add items to their cart.
When the cart is empty, a special section with id "empty-basket" is created. If the cart is not empty, this section's id becomes "basket".
I use Java and Selenium 3.9
These are my two selectors
#FindBy(how = How.CSS, using = "#empty-basket > section")
private WebElement conteneur_panier_vide;
#FindBy(how = How.CSS, using = "#basket > section:nth-child(1)")
private WebElement conteneur_panier_non_vide;
To check if the bloc containing the elements is well formed, I check if there is one of the sections described above. I use this piece of code to do so:
this.wait.until(
ExpectedConditions.or(
ExpectedConditions.visibilityOf(conteneur_panier_vide),
ExpectedConditions.visibilityOf(conteneur_panier_non_vide)
)
);
However, this gives me an error
org.openqa.selenium.TimeoutException: Expected condition failed: waiting for at least one condition to be
valid
Surprised, I tried this:
this.wait.until(ExpectedConditions.visibilityOf(conteneur_panier_vide))
on a page with an empty basket. It works, the WebElement is found.
I then tried this:
this.wait.until(
ExpectedConditions.or(
ExpectedConditions.visibilityOf(conteneur_panier_vide),
)
);
And it works as well. It means that adding a non-existing element to the 'or' breaks it, which is exactly the opposite of what it should be.
Does anyone have an idea of why my code is not working?
Edit: SOLVED!
The problem was that the element I was looking for was not on the page when the or function is called, resulting in the malfunctioning mentioned above.
I simply put
this.wait.until(ExpectedConditions.urlContains(MY_CART_URL));
which ensures the presence of one of the two sections.
There are two things you can do to clean this up:
Create a CSS selector that matches both elements
Use ExpectedConditions.visibilityOfElementLocated(By.css(...)) instead of the more complicated or
// CSS selectors can be separated by a comma (,)
String cssSelector = "#empty-basket > section, #basket > section:nth-child(1)";
this.wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(cssSelector));
Your original problem was that using the conteneur_panier_vide and conteneur_panier_non_vide attributes was causing Selenium to attempt to fetch them. You just need to wait for the appearance of either element.
Have you instantiated your wait object? You didn't specify your environment but here's what I do in
C#
var wait = new WebDriverWait( Driver, TimeSpan.FromSeconds( 10 ) );
Java
WebDriverWait wait = new WebDriverWait(driver, 20);
I did solve the problem, and edited the question to let people know.
TL;DR:
I needed some delay before calling the or function, and did so by waiting for the URL to be the right one.
Related
I have a page that randomly displays windows before homepage, to enter the site, I sometimes need to complete the form, so I use :
#FindBy(xpath = "")
public WebElement element;
Then i try to implement solution, but it fail.
if(element.isDisplayed()){
element.click();
}else if {
do something else // if not Displayed do something else and get test true?
}
Get error:
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element
In my project i use java, cucumber nad testNG. I have no idea how to do that, when it does not find a form on the page, it continues the test. Maybe I should do more methods or scenarios?
The error message means it cannot find the element.
Few things,
Do you have the right selector for that element?
Does the webpage use JQuery or AngularJS? If so, you need to wait for them to load
Are you using any type of wait? Before checking if the element exist on the page? (ex. ImplicitWait, FluentWait, Thread.sleep [bad practice])
Try this -> driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
This waits for an element to load up to 10 seconds before not finding it.
1) You need to use something like this:
public WebElement [] elements = driver.findElementsBy(xpath = "The Xpath you are looking for");
if (elements)
In case the randomly displayed window appear your list will not be empty, will contain an element you are looking for. Otherwise the list will be empty.
Your code fails since your are looking for an element that not always existing in the page.
2) You can also use Try - Catch
This is solution:
#FindAll(#FindBy(xpath = ""))
List<WebElement> buttonelements
if(!buttonelements.isEmpty()){
'code here'
}
Cause of a problem:
Using FindBy and method isDisplayed() throws NoSuchElementException if no matching elements are found.
Using FindAll and method isEmpty() don't throw any suchexception, it will return empty list.
i have the following issue, after perform the login, the system load the main page, its like 5 seconds doing it, so after that, the script should type over 3 fields and perform a tab to get more info, but the thing is that sometimes is working and sometimes does not, in 5 chances, only one worked, i do not know if its something about the time to get all fields or something like that, am using IE 9 cause the app only works on IE
HereĀ“s the code:
`
System.setProperty("webdriver.ie.driver","C:\\Apps\\eclipse\\IEDriverServer.ex;
WebDriver driver = new InternetExplorerDriver();
driver.get("http://15.192.41.95/Cabina/asp/Login.asp");
WebElement text1 = driver.findElement(By.id("text1"));
text1.sendKeys("xxxx");
WebElement password1 = driver.findElement(By.id("password1"));
password1.sendKeys("xxxx");
WebElement aceptar = driver.findElement(By.id("ok1"));
aceptar.click();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.switchTo().frame(driver.findElement(By.name("menu")));
WebElement txtNumPolSol = driver.findElement (By.id("txtNumPolSol"));
txtNumPolSol.sendKeys("877885");
WebElement Text8 = driver.findElement(By.name("txtNumofic"));
Text8.sendKeys("228");
WebElement txtCveInc = driver.findElement(By.name("txtCveInc"));
txtCveInc.sendKeys("1");
WebElement clave = driver.findElement(By.id("txtCveInc"));
clave.sendKeys(Keys.TAB);
driver.switchTo().frame(driver.findElement(By.name("dest")));
WebElement txtNomrepo = driver.findElement(By.id("txtNomrepo"));
txtNomrepo.sendKeys("Jorge Villarreal");
driver.findElement(By.id("txtRelacion")).sendKeys("Conductor");
WebElement txtTelrepo = driver.findElement(By.id("txtTelrepo"));
txtTelrepo.sendKeys("83029090");`
Here the different issues i got:
1) Unable to find element with name == txtOficina
2) Element is no longer valid
3) Unable to find element with id == txtCveInc (the field is there)
4) Unable to find element with name == txtCveInc (the field is there)
The steps that the system to get the issues are:
1) Log in (so far so good)
2) The system load the main page (the page has frames and gets all fields in 5 secs...)
3) The script type over the txtNumPolSol, txtNumofic and txtCveInc (most of the issues are in the last two fields)
4) The script performs a tab
5) The system load the some information regarding the record and the script continue...
Note: Almost all the issues occur on step 3...
Thanks for your feedback!
This sounds like a timing problem. Your simulated user is proceeding faster than the page becomes ready. Depending on the timing of the page loading, different problems occur.
The solution is to add a wait after steps that trigger a DOM change than influences your next step, but doesn't cause WebDriver to wait before returning. Google 'webdriver wait for element' to get lots of ways to do it.
I was also facing the similar kind of problem but i tried to find the elements with Css_Selectors and X paths instead of ids, that worked for me
The key here is to add atleast 1 second of implicit wait after every action, i.e. send keys etc. It will allow 'driver.findElement' enough time to find the element. Though I would suggest keeping this code in a testBase or a reusable method.
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
It will help in making your webtests less flaky.
I'm using webdriver(java) for a unique test where a page reloads itself when you log on, I've been unable to wait for the element to load because it is already there before the reload. So far the only way I've been able to get it to work is to use a thread.sleep, is there a way to listen for a page refresh?
One way to solve this is, to get a reference to the element you need, that appears both on the login-page and the reloaded page.
Then you can use the ExpectedConditions.stalenessOf to occur, and then you can be sure, that the element is removed from the DOM and a new element is created. Well, the last part is not guaranteed by this method, but at least you know that the old element is gone.
The code could look something like this:
WebElement elementOldPage = driver.findElement(By.id("yourid"));
... do login etc ...
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.stalenessOf(elementOldPage));
WebElement elementNewPage = driver.findElement(By.id("yourid"));
Building upon the accepted answer from Kim Schiller one might be interested in the following piece of code. It is surely not perfect due to the sleeps, so be free to suggest improvements to make it more bulletproof. Also note I'm no expert with selenium.
The if branch waits for the top level node in the html to go stale in case of a page reload. The else branch simply waits until the drivers url matches the request url in case we load a different page.
def safe_page_load(url):
if driver.current_url == url:
tmp = driver.find_element_by_xpath('/html')
driver.get(url)
WebDriverWait(driver, 2).until(ExpectedConditions.staleness_of(tmp))
else:
driver.get(url)
while(driver.current_url) != url:
sleep(0.3)
sleep(0.3)
Happy if I could help someone.
I am getting error "Element not found in the cache - perhaps the page has changed since it was looked up" , when i am trying to select an option from dropdown. Please find my code below
WebElement searchID=driver.findElement(By.xpath("ctl00$ContentPlaceHolderView$ctlRegistration$DropDownList1"));
List <WebElement> searchOptions=searchID.findElements(By.tagName("option"));
for(int i=0;i<searchOptions.size();i++)
{
if(i==1)
{
String searchIdDropdown=searchOptions.get(i).getText().toString().trim();
System.out.println(searchIdDropdown);
driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$DropDownList1")).click();
driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$DropDownList1")).sendKeys(searchIdDropdown);
driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$DropDownList1")).submit();
driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);
driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$TextBox1']")).sendKeys(Keys.TAB);
driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$TextBox1")).sendKeys(this.getProperties(Country+"_"+UserType+"_Registration_User1"));
driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$TextBox1")).click();
driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);
Please let me know how this error can be resolved.
Thanks in Advance,
Manasa.
So a few things to note:
If you can get them I would use IDs or Names for the elements you are
trying to find. Hopefully those will be much cleaner than the xpath.
You are rerunning your find element multiple times when you could
just use a WebElement and make your code far more readable.
I would try to use Explicit waits vs the implicit ones.
So this is my best suggestion of a starting point based on the code you provided.:
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement searchID = driver.findElement(By.xpath("ctl00$ContentPlaceHolderView$ctlRegistration$DropDownList1"));
List<WebElement> searchOptions=searchID.findElements(By.tagName("option"));
for(WebElement option : searchOptions)
{
if(option.getText().equals("drop down name"))// or since you were looking for the index of 1 regardless you can just do just to a .get(1) from the collection directly.
{
wait.until(ExpectedConditions.visibilityOf(option));
option.click();
WebElement textbox = driver.findElement(By.name("ctl00$ContentPlaceHolderView$ctlRegistration$TextBox1"));
wait.until(ExpectedConditions.visibilityOf(textbox));
textbox.sendKeys(this.getProperties(Country+"_"+UserType+"_Registration_User1"));
textbox.click(); //is this supposed to be a submit of some sort?
I want to find a element, xpath is like this
"//div[#id='contentPane']/div/div[2]/div/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div/div[3]/div/div"
and then do "clickAt"
If I use
driver.findElement(By.xpath("//div[#id='contentPane']/div/div[2]/div/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div/div[3]/div/div");
Actions clicker = new Actions(driver);
clicker.moveToElement(baseElement).moveByOffset(0, 0).click().perform();
I receive Exception "Element not found".
What I'm doing wrong?
The use of such long xpaths will cause test cases to fail,a new div can be added or removed dynamically.There should be some part of the id which remains constant.
For example in id = "someString_1234",if 'someString' remains constant and rest keeps changing,you can use symbols like id*='someString' to locate it.
You can refer this discussion for more info.
Instead of
driver.findElement(By.xpath("//div[#id='contentPane']/div/div[2]/div/div[2]/div[2]/div/div/div[2]/div/div/div/div/div/div/div[3]/div/div");
Try this code:
driver.findElement(By.id("contentPane");