Functionality: We have three fields Country, State, City -
After select the Country it reveal the States list and after select the state it reveal the city list.
Question: In this some states don't have cities, so how find country, states list those not have cities list using selenium?
i tried the code below like this but loop is not working & not shows the empty list
#Test
public void findstate() throws InterruptedException {
WebDriver driver = new FirefoxDriver();
driver.get("URL");
Select country = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCountryId']")));
List<WebElement> countrylist = country.getOptions();
System.out.println(countrylist.size());
for (int i = 1; i <= countrylist.size(); i++) {
countrylist.get(i).click();
Select state = new Select(driver.findElement(By.xpath("//*[#id='UserProfileStateId']")));
List<WebElement> statelist = state.getOptions();
for (int j = 1; j <= statelist.size(); j++) {
statelist.get(j).click();
Select city = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCityId']")));
List<WebElement> citylist = city.getOptions();
System.out.println("Country, state list of Empty Cities");
if (citylist.size() > 1) {
System.out.println(countrylist.get(i).getText() + "-----" + statelist.get(j).getText() + "-----");
}
}
}
}
Source Code for Country, State, City:
<span class="select-wrapper"><select name="data[UserProfile][country_id]" class="custom-select valid" id="UserProfileCountryId" aria-invalid="false" aria-required="true">
<option value="">Country</option>
<option value="1">Afghanistan</option>
<option value="2">Albania</option>
</select><span class="holder">Afghanistan</span></span>
<select name="data[UserProfile][state_id]" class="custom-select valid" id="UserProfileStateId" aria-invalid="false" aria-required="true">
<option value="">State</option>
<option value="42">Badakhshan</option>
<option value="43">Badgis</option>
<option value="44">Baglan</option>
</select>
<select name="data[UserProfile][city_id]" class="custom-select valid" id="UserProfileCityId" aria-invalid="false" aria-required="true">
<option value="">City</option>
<option value="5916">Andarab</option>
<option value="5917">Baghlan</option>
</select>
You have some typos due to copy&paste I think.
You need to use the correct Select variables in your loops, but you always use country.
Try this:
#Test
public void findstate() throws InterruptedException {
WebDriver driver = new FirefoxDriver();
driver.get("URL");
Select country = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCountryId']")));
List<WebElement> countrylist = country.getOptions();
System.out.println(countrylist.size());
for (int i = 1; i <= countrylist.size(); i++) {
countrylist.get(i).click();
Select state = new Select(driver.findElement(By.xpath("//*[#id='UserProfileStateId']")));
List<WebElement> statelist = state.getOptions();
for (int j = 1; j <= statelist.size(); j++) {
statelist.get(j).click();
Select city = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCityId']")));
List<WebElement> citylist = city.getOptions();
System.out.println("Country, state list of Empty Cities");
if (citylist.size() > 1) {
System.out.println(countrylist.get(i).getText() + "-----" + statelist.get(j).getText() + "-----");
}
}
}
}
To avoid this error it might be better to use foreach loops like this:
for (WebElement element : country.getOptions()) { }
I don't know your HTML code, but it might also be necessary to wait for the other dropdowns to fill with elements before asking getOptions(). Selenium is very very fast and it might ask for the options of the dropdown before they are loaded by your site. In this case you will not get any results even using the updated code.
Try to wait for the dropdowns to fill like this every time:
Select country = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCountryId']")));
List<WebElement> countrylist = country.getOptions();
System.out.println(countrylist.size());
for (WebElement element : countrylist) {
element.click();
Select state = new Select(driver.findElement(By.xpath("//*[#id='UserProfileStateId']")));
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(new Function<WebDriver, WebElement>() {
public WebElement apply(WebDriver webdriver) {
return state.getOptions() > 1;
}
});
List<WebElement> statelist = state.getOptions();
....
You do the same for city-options -> if you run into a timeout you found the state that has no cities...
Hi please try like below
#Test
public void findstate() throws InterruptedException {
WebDriver driver = new FirefoxDriver();
driver.get("URL");
WebDriverWait wait = new WebDriverWait(driver,20);
Select country = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCountryId']")));
List<WebElement> countrylist = country.getOptions();
System.out.println(countrylist.size());
for (int i = 0; i <= countrylist.size(); i++) {
countrylist.get(i).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[#id='UserProfileStateId']")));
Select state = new Select(driver.findElement(By.xpath("//*[#id='UserProfileStateId']")));
// here use state not country
List<WebElement> statelist = state.getOptions();
for (int j = 0; j <= statelist.size(); j++) {
statelist.get(j).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[#id='UserProfileCityId']")));
Select city = new Select(driver.findElement(By.xpath("//*[#id='UserProfileCityId']")));
// here use city not country
List<WebElement> citylist = city.getOptions();
for (int k = 0; k <= citylist.size(); k++) {
citylist.get(k).click();
System.out.println("Country, state list of Empty Cities");
if (citylist.get(k).getText() == null) {
System.out.println(countrylist.get(i).getText() + "-----" + statelist.get(j).getText() + "-----"+ citylist.get(k).getText());
}
}
}
}
}
In the outermost loop select country and then in an inner loop select each state for that country and check if city is found. If not then get the city & country & go on this way.
I think, it helps you.
its worked fine, add the 'tab' key after select the country
driver.findElement(By.xpath("//*#id='UserProfileCountryId']")).sendKeys(Keys.TAB);
But I got another problem while executing, After some time run the script i got error
org.openqa.selenium.StaleElementReferenceException: Element is no longer attached to the DOM
Command duration or timeout: 31 milliseconds
Thanks for answers
Related
I am automating a web application using Katalon studio with Selenium/Java. In a web table where we have to keep only first 15 records and rest of the records should be deleted. Each page contains 25 rows of data. The following is the code I have written but when I execute it deletes only 25 records and it stops. The code must delete the rows till it reaches 15, e.g. from 200 records to 15 records.
public void deleteRows() {
int rows_count;
int Row;
WebElement UsersTable;
WebElement UsersTable2;
List<WebElement> rows_UsersTable;
List<WebElement> cols_UserTable;
int cols_count;
int rowsNeeded = 15;
WebDriverWait wait;
List<WebElement> rows_UsersTable2;
UsersTable = driver.findElement(By.xpath("//table/tbody"));
rows_UsersTable = UsersTable.findElements(By.tagName("tr"));
rows_count = rows_UsersTable.size();
println ("Total rows is " +rows_count)
cols_UserTable = UsersTable.findElements(By.xpath("//table/tbody/tr[1]/td"));
cols_count = cols_UserTable.size();
println ("Total columns is " +cols_count)
if (rows_count >= rowsNeeded) {
int j = 1;
int newRowCount;
while (j <= rows_count) {
WebElement deleteIcon = driver.findElement(By.xpath("(//table/tbody/tr/td)[last()]/span"));
String text = deleteIcon.getText();
deleteIcon.click();
Thread.sleep(2000);
WebElement confirmBtn = driver.findElement(By.xpath("//button[text()='Confirm']"));
confirmBtn.click();
Thread.sleep(3000);
println ("Successfully deleted " + j);
UsersTable2 = driver.findElement(By.xpath("//table/tbody"));
rows_UsersTable2 = UsersTable.findElements(By.tagName("tr"));
newRowCount = rows_UsersTable2.size();
println ("The new row count is " + newRowCount)
j++;
}
}
}
I know there is something I have missed, please help.
Thanks
#Test
public void homework() throws InterruptedException {
driver.get("http://www.localhost/litecart/admin/");
driver.findElement(By.cssSelector("td [name = username]")).sendKeys("admin");
driver.findElement(By.cssSelector("td [name = password]")).sendKeys("admin");
driver.findElement(By.cssSelector("div.footer [name = login]")).click();
List<WebElement> elements = driver.findElements(By.cssSelector("ul#box-apps-menu > li"));
for (WebElement we : elements) {
we.click();
}
}
This is my code. elements i want to click
I only click the first item and then i get this "stale element reference: element is not attached to the page document".
Once you click on a list item, it expands and few more child list-items appear, so idk if that's what's causing the issue.
EDIT:
Here's how i did it, i included even child elements. Thanks to the comments below i managed to finally complete this task.
public class HomeWork1 extends TestBase {
#Test
public void homework() throws InterruptedException {
driver.get("http://www.localhost/litecart/admin/");
driver.findElement(By.cssSelector("td [name = username]")).sendKeys("admin");
driver.findElement(By.cssSelector("td [name = password]")).sendKeys("admin");
driver.findElement(By.cssSelector("div.footer [name = login]")).click();
List<WebElement> elements = driver.findElements(By.xpath(("//ul//li")));
for (int i = 1; i <= elements.size(); i++) {
driver.findElement(By.xpath("//ul[#id='box-apps-menu']/li["+i+"]")).click();
List<WebElement> element = driver.findElements(By.xpath("//ul[#class='docs']/li"));
for (int j = 1; j < element.size() + 1; j++){
driver.findElement(By.xpath("//ul[#class='docs']//li["+j+"]")).click();
}
if (i == 17) break;
}
}
}
or Try this
#Test
public void homework() throws InterruptedException {
driver.get("http://www.localhost/litecart/admin/");
driver.findElement(By.cssSelector("td [name = username]")).sendKeys("admin");
driver.findElement(By.cssSelector("td [name = password]")).sendKeys("admin");
driver.findElement(By.cssSelector("div.footer [name = login]")).click();
List<WebElement> elements = driver.findElements(By.xpath("//ul//li"));
// elements.size() will give you the total number of elements.
for (i=1;i<=elements.size(),i++) {// This will iterate through all the elements
driver.findElement(By.xpath("//ul/li["+i+"]")).click(); // clicking on each li element one by one
//include wait here
}
}
Yes, you can apply the same logic if you want to work with child items. For example you can use following for first parent item:
List<WebElement> elements = driver.findElements(By.xpath("//ul/li[1]/**")); // ** can be replaced by child identifiers
I have a code in which I traverse table rows and columns, and I'd like to add it's values to a list.
It takes me a significant amount of time.
So I added a time measurement, and I noticed that for some reason the time increases from row to row.
I cannot understand why.
Can you advise please?
private void buildTableDataMap() {
WebElement table = chromeWebDriver.findElement(By.id("table-type-1"));
List<WebElement> rows = table.findElements(By.tagName("tr"));
theMap.getInstance().clear();
String item;
for (WebElement row : rows) {
ArrayList<String> values = new ArrayList<>();
List<WebElement> tds = row.findElements(By.tagName("td"));
if(tds.size() > 0){
WebElement last = tds.get(tds.size() - 1);
long time = System.currentTimeMillis();
values.addAll(tds.stream().map(e->e.getText()).collect(Collectors.toList()));
System.out.println(System.currentTimeMillis() - time);
//remove redundant last entry:
values.remove(tds.size() - 1);
callSomeFunc(values, last);
item = tds.get(TABLE_COLUMNS.NAME_COL.getNumVal()).getText();
item = item.replaceAll("[^.\\- /'&A-Za-z0-9]", "").trim();//remove redundant chars
theMap.getInstance().getMap().put(item, values);
}
}
}
Guys, I continued researching.
First of all, Florent's kind answer did not help me because, at lease as I understand, It returned me an array list of strings which I had to parse, and I don't like this kind of solution too much...
So I nailed the problem in finding that the e.getText() call was increasing in time from call to call!!!
I also tried e.getAttribute("innerText") instead but no change.
Can't understand why. Any idea to solve?
WebElement last = null;
for (WebElement e : tds){
last = e;
long tm1 = 0, tm2 = 0;
if(Settings.verboseYN) {
tm1 = System.currentTimeMillis();
}
s = e.getText(); //This action increases in time!!!
if(Settings.verboseYN) {
tm2 = System.currentTimeMillis();
}
values.add(s); //a 0 ms action!!!
if(Settings.verboseYN) {
System.out.println("e.getText()) took " + (tm2 - tm1) + " ms...");
}
}
That is an graph of the time getText took...
08-May-18
Another source of growing execution time is this one:
void func(WebElement anchorsElement){
List<WebElement> anchors = anchorsElement.findElements(By.tagName("a"));
for (WebElement a : anchors) {
if (a.getAttribute("class").indexOf("a") > 0)
values.add("A");
else if (a.getAttribute("class").indexOf("b") > 0)
values.add("B");
else if (a.getAttribute("class").indexOf("c") > 0)
values.add("C");
}
}
Every functions has 5 iterations only, but still each call to the function increases its execution time.
Is there a solution for this one as well?
Calling the driver is an expensive operation. To significantly reduce the execution time, use a JavaScript injection with executeScript to read the whole table in a single call. Then process/filter the data on the client side with Java.
public ArrayList<?> readTable(WebElement table)
{
final String JS_READ_CELLS =
"var table = arguments[0]; " +
"return map(table.querySelectorAll('tr'), readRow); " +
"function readRow(row) { return map(row.querySelectorAll('td'), readCell) }; " +
"function readCell(cell) { return cell.innerText }; " +
"function map(items, fn) { return Array.prototype.map.call(items, fn) }; " ;
WebDriver driver = ((RemoteWebElement)table).getWrappedDriver();
Object result = ((JavascriptExecutor)driver).executeScript(JS_READ_CELLS, table);
return (ArrayList<?>)result;
}
The problem you are facing is because of the way Selenium works by design. Let's look at how a JavaScript get's executed or a operation is performed
tds.get(TABLE_COLUMNS.NAME_COL.getNumVal()).getText();
You have a collection of objects. Each object is assigned a unique ID on the browser side by the selenium driver
So when you do a getText() below is what happens
Your code -> HTTP Request -> Browser Driver -> Browser ->
|
<---------------------------------------------
Now if you have a table of 400rx10c then it accounts to 4000 HTTP calls, even if one call takes 10ms, we are looking at a 40000ms~=40sec, which is a decent delay to read a table
So what you want to do is to get all the data in single go by executing a javascript which give you 2d array back. It is quite simple, I found a code on below site
http://cwestblog.com/2016/08/21/javascript-snippet-convert-html-table-to-2d-array/
function tableToArray(tbl, opt_cellValueGetter) {
opt_cellValueGetter = opt_cellValueGetter || function(td) { return td.textContent || td.innerText; };
var twoD = [];
for (var rowCount = tbl.rows.length, rowIndex = 0; rowIndex < rowCount; rowIndex++) {
twoD.push([]);
}
for (var rowIndex = 0, tr; rowIndex < rowCount; rowIndex++) {
var tr = tbl.rows[rowIndex];
for (var colIndex = 0, colCount = tr.cells.length, offset = 0; colIndex < colCount; colIndex++) {
var td = tr.cells[colIndex], text = opt_cellValueGetter(td, colIndex, rowIndex, tbl);
while (twoD[rowIndex].hasOwnProperty(colIndex + offset)) {
offset++;
}
for (var i = 0, colSpan = parseInt(td.colSpan, 10) || 1; i < colSpan; i++) {
for (var j = 0, rowSpan = parseInt(td.rowSpan, 10) || 1; j < rowSpan; j++) {
twoD[rowIndex + j][colIndex + offset + i] = text;
}
}
}
}
return twoD;
}
I assume you store the above script in a SCRIPT variable and then you can run it like below
WebDriver driver = ((RemoteWebElement)table).getWrappedDriver();
Object result = ((JavascriptExecutor)driver).executeScript(SCRIPT + "\n return tableToArray(arguments[0]);" , table);
This will get you a 2D array of the data and you can then process it the way you like it
Why click() doesn't works?
website:
String startPage = "http://www.domiporta.pl/mieszkanie/sprzedam?Localization=dolno%C5%9Bl%C4%85skie&PageNumber=24&SortingOrder=InsertionDate";
Code:
List<WebElement> RowsMain = driver.findElements(By.className("detail-card__heading"));
for(int i=0;i<RowsMain.size();i++){
driver.get(startPage);
List<WebElement> rows = driver.findElements(By.className("detail-card__heading"));
List<WebElement> cols=new ArrayList<WebElement>();
cols=rows.get(i).findElements(By.tagName("div"));
for(WebElement col:cols) {
col.click();
}
}
Why click doesn't workS?
It is error because you navigate out of the current page, so DOM has been destroyed. The solution is simple, find the elements every time when you navigate out of the page. See code below.
int rowCount = driver.findElements(By.className("detail-card__heading")).size();
for(int i=0; i<rowCount; i++){
List<WebElement> rows = driver.findElements(By.className("detail-card__heading"));
int colsCount = rows.get(i).findElements(By.tagName("div")).size();
for(int j=0;j<colsCount; j++) {
rows = driver.findElements(By.className("detail-card__heading"));
List<WebElement>cols = rows.get(i).findElements(By.tagName("div"));
cols.get(j).click();
driver.get(startPage);
}
}
I am working in list view. In it, there are two lists such that if I select a value from base list, it will add to the child list and remove the item from base list, all are working fine. My query is delete list item from child list it will re-add to the base list.
contacts = jsonObj.getJSONArray(TAG_PRODUCTLIST);
for (int i = 0; i < contacts.length(); i++)
{
JSONObject c = contacts.getJSONObject(i);
Object id = c.get(TAG_CATID);
Object em = c.get(TAG_CAT);
contact1 = new HashMap<>();
contact1.put(TAG_CATID, id);
contact1.put(TAG_CAT, em);
JSONArray productss = c.getJSONArray(TAG_PRODUCT);
worldpopulationlist.add(new QuoteSectionItem(em.toString()));//listview section
for (int j = 0; j < productss.length(); j++)
{
JSONObject d = productss.getJSONObject(j);
Object pid = d.get(TAG_PID);
Object price = d.get(TAG_PRICE);
Object pn = d.get(TAG_PNAME);
contact = new HashMap<>();
contact.put(TAG_PID, pid);
contact.put(TAG_PRICE, price);
contact.put(TAG_PNAME, pn);
contact.put(TAG_PDESC, pn);
Quote_Products worldpopulation = new Quote_Products(id,em,pid,pn,price);//listview item
worldpopulationlist.add(worldpopulation);
} }
this is base list creation and i create child list like` SparseBooleanArray selected = listviewadapter.getSelectedIds();
for (int i = (selected.size() - 1); i >= 0; i--)
{
if (selected.valueAt(i))
{
QuoteItem selectedI = listviewadapter.getItem(selected.keyAt(i));
Quote_Products selecteditem = (Quote_Products) selectedI;
System.out.println("Selected Items : "+selecteditem.getCountry());
System.out.println("Selected Items : "+selecteditem.getRank());
System.out.println("Selected Items : "+selecteditem.getPopulation());
System.out.println("Products:"+product);
productList.add(product);
Quote_SelectedProducts worldpopulation1 = new Quote_SelectedProducts(
selecteditem.getCidid(),
selecteditem.getCname(),
selecteditem.getRank(),
selecteditem.getCountry(),
selecteditem.getPopulation());
worldpopulationlist1.add(worldpopulation1);
listviewadapter.remove(selecteditem);
}
}
`
after this are successfully done i need to to how to to reinsert the list value once delete value form child list
SparseBooleanArray selected = listviewadapter1.getSelectedIds();
for (int i = (selected.size() - 1); i >= 0; i--)
{
if (selected.valueAt(i))
{
Quote_SelectedProducts selecteditem = listviewadapter1.getItem(selected.keyAt(i));
listviewadapter1.remove(selecteditem);
System.out.println("Delete after ignore "+selecteditem.getCidid());
System.out.println("Delete after ignore "+selecteditem.getCname());
System.out.println("Delete after ignore "+selecteditem.getRank());
System.out.println("Delete after ignore "+selecteditem.getCountry());
System.out.println("Delet after ignore "+selecteditem.getPopulation());
Quote_Products worldpopulation = new Quote_Products(selecteditem.getCidid(),
selecteditem.getCname(),selecteditem.getRank(),
selecteditem.getRank(),selecteditem.getPopulation());
System.out.println(" EEE "+String.valueOf(worldpopulation));
worldpopulationlist.add(worldpopulation);
System.out.println(" EEE "+String.valueOf(worldpopulationlist));
}
the value delete from child list how to re insert the child list value in the base list
Thanks in advance