Is there any way to reduce element locating time in Selenium WebDriver? - java

I have one select city dropdown and there are around 8000 city presents. My usecase are:
Get the selected city name
If city name is not as expected then select the desired city
Now again get the city name to verify that desired city has been selected
My code is working fine but the issue is to complete these 3 steps it takes around 5-8 minutes. I know its due to the large set of city name available in dropdown
This is the dropdown HTML structure :
<div class="col-md-12">
<label class="mmk-filter-control">Preferred Source City : </label>
<div class="pull-right refe-link">
<div class="mmk-filter-control mmk-select-filter pull-right mr5">
<select id="ddlPrefferedSourceCity" class="form-control" name="ddlPrefferedSourceCity">
<option value="-1">- Select -</option>
<option value="A.S.Peta Bypass">A.S.Peta Bypass</option>
<option value="aadsar">aadsar</option>
<option value="aagariya">aagariya</option>
<option value="aahur">aahur</option>
<option value="aakadiya">aakadiya</option>
<option value="Aala">Aala</option>
<option value="Aanjangaon">Aanjangaon</option>
.
.
around 8000 options
And code is:
if(usersname.size()>0)
{
Select s = new Select(preferredCity);
Comman.wait.until(ExpectedConditions.invisibilityOf(loader));
String cityName = s.getFirstSelectedOption().getText();
if(cityName.equals(preferredCityName))
{
LogWriter.logger.info("Preferred City is already Selected");
TakeScreenshot.passedScreenShot();
}
else
{
Comman.wait.until(ExpectedConditions.invisibilityOf(loader));
LogWriter.logger.info("Last Selected Preferred City is : " + s.getFirstSelectedOption().getText());
TakeScreenshot.passedScreenShot();
s.selectByVisibleText(preferredCityName);
setPreferenceButton.click();
Comman.wait.until(ExpectedConditions.invisibilityOf(loader));
LogWriter.logger.info("New Selected Preferred City is : " + s.getFirstSelectedOption().getText());
TakeScreenshot.passedScreenShot();
}
}
Is there any way to overcome and make the test fast?

The issue is likely due to getFirstSelectedOption since the method sends the isSelected command for each option:
https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/support/ui/Select.java#L93
So instead of :
s.getFirstSelectedOption().getText()
, I would use a selector to get the first selected option:
preferredCity.findElement(By.cssSelector("option[selected]")).getText()
Update
The selected attribute is not updated in the DOM when the selection changes.
So an alternative would be to read the selectedIndex property on <select> :
int selectedIndex = Integer.parseInt(preferredCity.getAttribute("selectedIndex"));
WebElement selectedOption = preferredCity.findElement(By.cssSelector(
String.format("option:nth-child(%s)", selectedIndex + 1)));
String text = selectedOption.getText();
or with executeScript:
String JS_GET_FIRST_SELECTED_OPTION =
"var e=arguments[0], i=e.selectedIndex; return i < 0 ? null : e.options[i];";
JavascriptExecutor jse = (JavascriptExecutor)driver;
WebElement selectedOption = (WebElement)jse.executeScript(JS_GET_FIRST_SELECTED_OPTION, preferredCity);
if (selectedOption == null)
throw new NoSuchElementException("No options are selected");)
String text = selectedOption.getText();

While 8000 cities in a single dropdown is a questionable design, it might not be the whole issue. I created a simple HTML file that contained 100,000 options and selected a value from that list and it was complete in 12s, including loading the entire page. Granted it wasn't hosted on the web. Your internet connection and/or the site's speed may be factors.
I would look at a few things:
You seem to be using WebDriverWait in Comman.wait (Common is misspelled, btw). Are you also using an implicit wait? If so, the docs state not to mix both.
WARNING: Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times.
If you are, remove the implicit wait and see if that helps.
Next I would try getting some timings. Add a timing between each of the steps to see where the long waits lie. With that info, you will have a better idea of where the delays are coming from and how to address them.
You are waiting for the loader to be invisible more than I think is necessary. It shouldn't matter but removing some of them might help, especially if you are using an implicit wait (see #1). You wait for it after you grab the SELECT, which shouldn't be needed. You wait for it again in the first line of the else but you haven't done anything that should trigger a loader. The only place that seems reasonable is the 3rd instance, after you select the item from the dropdown.
Have you tried selecting by value instead of by visible text? It may not matter but I'm not really sure how it finds visible text.
Another option is to avoid use of the Select class altogether. While it's a good practice to use it normally because it makes dealing with SELECT so much easier, in this case it may be contributing to the performance issue.
One way to get around it may be to go after the option directly using a CSS selector, e.g. #ddlPrefferedSourceCity > option[value='" + preferredCityName + "']. I tried this on my local machine and it was slightly faster... but it's a difference of 1.2s vs .6s.

I really don't know why it is taking time to get the dropdown value using getFirstSelectedOption
I have tried JavascriptExecutor where i have populated value of the dropdown using jQuery command
public String getDropdownValue()
{
JavascriptExecutor e = (JavascriptExecutor) driver;
return (String) e.executeScript("return $('#ddlPrefferedSourceCity').val();");
}
Now I'm able to get the value even change the dropdown value and again find the selected value. And now the time is 5-8 second.

Related

How to locate an element on this page?

I tried a lot to locate elements on this page with this link
ALL I want to do is to select "1 queen bed" or "1 double large bed" and then select amount from the drop down list then press, I'll reserve button.
But I totally failed trying all of these:-
using Action --> moveToElement --> perform()
using JS Execution --> scrollToView, scrollBy(0,100) for example to make the page scroll down
using all types of locators (ID, xPaths, cssSelectors, names) but always an error message that Expected condition failed: waiting for visibility of element located by By.xpath- By.id .. etc.
tried to search for an iFrame but I didn't find but I think there are some but not related to the locators I want
Thread.sleep() to wait for a time if the element is not loaded on the web page.
I am using Selenium JAVA TestNG
so in the page I write this function:-
Page class I write this function :-
private By btnBed = By.xpath("(//i[#class='bicon bicon-double'])[1]");
public void clickBed(){
// JavascriptExecutor exe = (JavascriptExecutor)driver;
// exe.executeScript("window.scrollBy(0,1000)");
click(btnBed);
}
In my Test I write this:-
hotelPage.clickBed();
Error message:-
Expected condition failed: waiting for visibility of element located by By.xpath
The problem was that this page was opened in a new tab so the code can't locate any elements on the screen.
So I just switch tabs, the code works fine.
ALL I want to do is to select "1 queen bed" or "1 double large bed"
I inspecting the DOM on the link you provided, I see that it is radio button that you might want to select. Try locate the radio element button something like this
<input type="radio" value="1" name="bedPreference_78883120" data-bed-type="">
Locate by name
By.name("bedPreference_78883120")
then perform the click.

Unable to replace value in a table column

Unable to send values in the table column. I got different errors while I am trying to insert a value in the column using Selenium.
I tried to set a new value in the table's column. It shows the error as The element must be user-editable in order to clear it.
WebElement.clear();
WebElement.sendKeys("value");
(or)
WebElement.sendKeys(Keys.DELETE);
WebElement.sendKeys("value");
Then to click and edit the value.
Actions actions = new Actions(getWebDriverEx());
WebElement TableColumn = Driver.findElement(By.id("element"));
actions.moveToElement(TableColumn);
actions.click().build().perform();
actions.sendKeys(Keys.BACK_SPACE+b+b);
actions.sendKeys("value");
The value which was passed is not inserted in the Tables column. But I can able to click the Tables column. Here my test passed.
Then tried to set value. It shows the error as timed out.
WebElement.sendKeys(Keys.DELETE);
WebElement.sendKeys("15000");
Again I used the div/span combination as XPath and I have edited the value. But it does not reflect in the table.
JavascriptExecutor js = (JavascriptExecutor) getDriver();
js.executeScript("document.getElementById('element').innerHTML="+15000);
Here I do not get any errors. But the value not reflected after save.
I gave element to various formats.
div//[id]
div//span
XPath
id alone (which was in the div)
HTML:
<div id="element" class="tables_second_column">
<div class="class_name">
<div class="class_name">
<div class="class_name"><span>5000</span></div>
</div>
</div>
</div>
Try on the following and it's working for me:
js = "document.querySelector('#element .class_name .class_name .class_name>span').innerHTML = '15000';"
driver.execute_script(js)
Hope it helps you!
This should be sufficient
WebElement textBox = driver.findElement(By.xpath("//span[text()='5000']"));
textBox.clear();
textBox.sendKeys("");
textBox.sendKeys("15000");
I do send a empty space in order to get the textbox active as sometime the DOM might not reflect immediately as this element is quiet nested.
As per the HTML you shared, the table column is within a <span> which is inside several <div> tags. Hence we need to construct an unique xpath to identify the WebElement and first send clear() method then use sendKeys() method as follows :
driver.findElement(By.xpath("//div[#id='element']/div[#class='class_name']/div[#class='class_name']/div[#class='class_name']/span[text()='5000']")).clear();
driver.findElement(By.xpath("//div[#id='element']/div[#class='class_name']/div[#class='class_name']/div[#class='class_name']/span[text()='5000']")).sendKeys("15000");
Update
As you mentioned Text which was passed in the span is not predictable so we would simply omit the clause [text()='5000']. As you have provided a sample HTML with sample classnames I have constructed a nearly absolute xpath. So our code will be :
driver.findElement(By.xpath("//div[#id='element']/div[#class='class_name']/div[#class='class_name']/div[#class='class_name']/span")).clear();
driver.findElement(By.xpath("//div[#id='element']/div[#class='class_name']/div[#class='class_name']/div[#class='class_name']/span")).sendKeys("15000");
Thanks for your reply
It works for me with the below method
webElement.sendKeysByAction(value)

Selenium sees textbox as hidden even if I can see it in the browser

I have a password textbox that is something like this
<input class="blahblah" id="someId" type="password"></input>
I am able to see this textbox in the browser and am able to manually insert password.
However when I test this ui using selenium, although it finds the element correctly, but when it tries to click the element, it throws an error
"org.openqa.selenium.ElementNotVisibleException: Element is not currently visible and so may not be interacted with"
I did a check in code using
Boolean isDisplayed=el.isDisplayed();//false
Boolean isEnabled=el.isEnabled();//true
The isDisplayed came up false and isEnabled came up true. There is a 15 second delay added to give the page enough time to load (the page loads instantly). So adding a delay will not fix the problem.
I verified using firefox developer tools that the id it was finding was of the correct element.
Why does selenium think its invisible even if I am able to see it in the browser? Could it be that one of the parent elements has some style attribute that selenium doesn't like? Or is it a bug in the selenium driver?
I am using selenium driver for java version 2.45.0
The problem is that the desired input is really invisible due to the display: none being set on it's parent table:
<table title="Type a password."
class="dxeTextBoxSys dxeTextBox_MyCompany "
id="ctl00_ctl00_MasterContent_MainContentPlaceHolder_ViewCredentials_TopicPanel1_credentialGrid_editnew_4_txtPassword_P_PB"
style="width: 100%; border-collapse: collapse; display: none;"
border="0" cellpadding="0" cellspacing="0">
Most likely, the table is becoming visible on a particular user action that you need to determine.
But, alternatively, you can make the table visible through javascript:
WebElement table = driver.findElement(By.id("ctl00_ctl00_MasterContent_MainContentPlaceHolder_ViewCredentials_TopicPanel1_credentialGrid_editnew_4_txtPassword_P_PB"));
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("arguments[0].style.display = 'block';", table);
In case the above doesn't make any difference.
There is an another hidden password input that can be important:
<input value=""
name="ctl00$ctl00$MasterContent$MainContentPlaceHolder$ViewCredentials$TopicPanel1$credentialGrid$editnew_4$txtPassword$P$PB$CVS"
type="hidden">
You can try making it visible and sending keys to it:
WebElement password = driver.findElement(By.name("ctl00$ctl00$MasterContent$MainContentPlaceHolder$ViewCredentials$TopicPanel1$credentialGrid$editnew_4$txtPassword$P$PB$CVS"));
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("arguments[0].setAttribute('type', 'text');", password);
password.sendKeys("MyPassword");
In case the above doesn't work.
You can set the input value through javascript:
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("document.getElementById('ctl00_ctl00_MasterContent_MainContentPlaceHolder_ViewCredentials_TopicPanel1_credentialGrid_editnew_4_txtPassword_P_PB_I').setAttribute('value', 'MyPassword');");
Possibly, Selenium is going too fast through your DOM. Has happened with me several times and your element hasn't fully loaded into DOM.
I am more familiar with the PHP/PHPUnit libraries available for Selenium, but perhaps you can introduce a temporary wait with a command similar to waitForElementPresent.
Also, if you have control of the code, can you give a 'name' attribute to your input field as well? It could not hurt anything to do so.
Have a look at the DOM elements and verify that there is no parent element with a display: none etc, when i encountered an issue like this that was the problem.
Are you able to get information from the element by XPath? This was my work around.
I have faced this kind of issue many times. the first thing comes into my mind is probably the selector you are using is not unique or not returning THE element you are looking for. Since,
Boolean isDisplayed=el.isDisplayed();//false
Boolean isEnabled=el.isEnabled();//true
does not return NoSuchElement exception I do not think it's a element load issue. A quick check can tell you what's going on
driver.findElements(By.cssSelector("the css")).size(); and see how many count it returns.

How best should I interact with a this <li> from Selenium Java Webdriver

I am trying to interact with the Nike shoe online shop, login and then select a shoe size from the list and then press add to cart button from Selenium's Java WebDriver:
http://store.nike.com/us/en_us/pd/air-max-2014-running-shoe/pid-830258/pgid-774364
First of all I cannot seem to find the correct <li> element and select it - and need some advise on how to do it.
I am finding my code does not work for selecting a shoe size : pastebin.com/6K1RpPKL (as guided by the kind user on the first response.
The element type in li is not select. Use following code instead, it will work fine.
WebElement shoeSizes = driver.findElement(By.xpath("//div[contains(#class,'exp-pdp-size-container')]/a"));
shoeSizes.click(); // Expanded
String shoeSize = "8.5";
WebElement shoeSizeSel = driver.findElement(By.xpath("//li[text()='"+shoeSize+"']"));
shoeSizeSel.click(); // Size selected
driver.findElement(By.xpath("//div[#class='exp-pdp-save-container']/button")).click(); // Added to cart
As far as advice goes, you should first learn the basics like identifying elements, using locators before asking these kind of question. Go through these: Selenium Docs, Mozilla Blogs. Many such resources are available on web.
First off, you should get away from the IDE if you are wanting more robust test-writing like flash. For your login issue, this is simple.
Using the getting started with selenium framework your test would look like:
#Config(url="http://store.nike.com/us/en_us/pd/air-max-2014-running-shoe/pid-830258/pgid-774364")
public class NikeTest extends AutomationTest {
#Test
public void testNike() {
click (By.cssSelector("div.login.nav-section > a"))
.setText(By.cssSelector("form[name='login-form] input[name='email']"), "<My Username>")
.setText(By.cssSelector("form[name='login-form] input[name='password']"), "<My Password>")
.click (By.cssSelector("form[name='login-form] button.exp-login-submit")
// now we're logged in.
// let's select a size of shoe.
.click (By.cssSelector("div.exp-pdp-size-and-quantity-container > a.exp-pdp-size-dropdown") // now it's expanded.
.selectOptionByText(By.cssSelector("select[name='skuAndSize']"), "10.5") // you can replace 10.5 with whatever option you need.
}
}
Those are some CSS selectors you can use. Also per your Flash thing, i think you're out of luck buddy.. I hadn't heard of any very successful solution with automating flash.
One key thing here:
Make sure you know what element is receiving the click. Selenium IDE doesn't do a great job determining WHAT exact element is receiving the click. My guess is that it was trying either the <div> or <li> when it's the <a> that actually makes the dropdown come down.

Selenium: find element "next to" other element

I'm adding web tests to my project using Selenium. I already have a bunch of tests that check for a specific element using:
final WebElement dateElement = web.findElement(By.id(elementId));
And this works fine.
Now I have another requirement. This is in my generated page:
<input type="text" id="dateElement" name="dateElement" value="bunch of monkeys" tabindex="101" placeholder="yyyy-mm-dd">
<span class="error">dateElement is an invalid date</span>
How can I get hold of the error message?
I'd like something that allows me to request the span element with class "error" that is just after dateElement.
(This error message was ganerated by Spring MVC, so it's not easy to change it directly. Possible I guess, but I'd prefer not).
Any alternative idea is welcome.
OK, I already found a solution using Xpath and following-sibling, it wasn't too complicated.
final WebElement errorElement = web.findElement(By.xpath("//*[#id='" + elementId + "']/following-sibling::span[#class='error']"));
This gives me what I wanted, and throws a NoSuchElementException when it's not here, which is exactly what I want.
elementSelector = "input + span[class='error']";
final WebElement dateElement = web.findElement(By.cssSelector(elementSelector));

Categories