I'm having a problem with my tests with Selenium webdriver. I'm using Java. Can't select from a multiple drop-down that is not class Select. This is how the drop-down looks like:
Drop-Down picture
And that's the code:
<div class="form-group ">
<label for="CurrentCategoriesNomIds-selectized">Categories</label>
<select placeholder="" multiple="multiple" id="CurrentCategoriesNomIds" name="CurrentCategoriesNomIds" tabindex="-1" class="selectized" style="display: none;">
<option value="325" selected="selected">Education</option>
</select>
<div class="selectize-control multi plugin-remove_button">
<div class="selectize-input items not-full has-options has-items">
<div class="item" data-value="325">
Education
×
</div>
<input type="text" autocomplete="off" tabindex="" id="CurrentCategoriesNomIds-selectized" style="width: 4px; opacity: 1; position: relative; left: 0px;"></div>
<div class="selectize-dropdown multi plugin-remove_button" style="display: none; visibility: visible; width: 800px; top: 36px; left: 0px;">
<div class="selectize-dropdown-content">
<div class="option" data-selectable="" data-value="324">Agriculture</div>
<div class="option" data-selectable="" data-value="298">Culture</div>
<div class="option" data-selectable="" data-value="326">Employment</div>
<div class="option" data-selectable="" data-value="323">Environment</div>
<div class="option" data-selectable="" data-value="327">Other</div>
<div class="option" data-selectable="" data-value="297">Political</div>
<div class="option" data-selectable="" data-value="322">Transport</div>
</div>
</div>
</div>
</div>
This is how it looks like when 2 options are selected. I was wondering if I can try with KEYS but the page doesn't work like that. Haven't seen that kind of field before, and not sure how to proceed?
You can click on dropdown using this code :
public static void selectOption(WebDriver driver, String optionName) {
List<WebElement> options = driver.findElements(By.xpath("//div[#class='selectize-dropdoun-content']//div[#class='option']"));
options.forEach(option -> {
if (option.getAttribute("innerText").equals(optionName)) {
Actions actions = new Actions(driver);
actions.moveToElement(option).click().build().perform();
}
});
}
and then use like this:
String option = "Education";
selectOption(driver,option);
Hope that helps you:)
Adding screenshot for what I have tried on website : https://semantic-ui.com/modules/dropdown.html
I don't much use Java, so I'll be writing some pseudo code for this that should give you an outline on how to achieve it (but may not run as written).
public static void selectOptionFromSelectizeDropdown(String optionText, String dropdown){
boolean completed = false;
int numberOfOptions = driver.findElements(By.css(dropdown + " .option")).length
for(int i = 0; i < numberOfOptions && completed === false; i++){
// Check if it's displayed, if it is, HUZZAH! Click the option
if(driver.findElement(By.xpath('//*/*[contains(#class, "option") and contains(text(), "'+optionText+'")])')).isDisplayed()){
driver.findElement(By.xpath('//*/*[contains(#class, "option") and contains(text(), "'+optionText+'")])')).click();
completed === true;
break;
} else {
// In case there are many options, and you have to scroll through them.
int x = 0;
while(x <= 6){
driver.findElement(By.css(dropdown)).sendKeys(Keys.DOWN);
i++;
}
}
if(i===numberOfOptions - 1){
throw new Error("Option Not Found");
}
}
}
selectOptionFromSelectizeDropdown("Education", ".selectize-dropdown-content");
If this doesn't work, I'd recommend changing the click() to a sendKeys(Key.ENTER) to see if that would work.
Explanation
Will loop through, seeing if the option is displayed on the page. If not, will scroll down x times, and check again, until the option is found.
If it reaches the number of options inside the box, it will throw an error.
Related
Hi guys I'm using jsoup in a java webapplication on IntelliJ. I'm trying to scrape data of port call events from a shiptracking website and store the data in a mySQL database. The data for the events is organised in divs with the class name table-group and the values are in another div with the class name table-row. My problem is the divs rows for all the vessel are all the same class name and im trying to loop through each row and push the data to a database. So far i have managed to create a java class to scrape the first row. How can i loop through each row and store those values to my database. Should i create an array list to store the values?
this is my scraper class
public class Scarper {
private static Document doc;
public static void main(String[] args) {
final String url =
"https://www.myshiptracking.com/ports-arrivals-departures/?mmsi=&pid=277&type=0&time=&pp=20";
try {
doc = Jsoup.connect(url).get();
} catch (IOException e) {
e.printStackTrace();
}
Events();
}
public static void Events() {
Elements elm = doc.select("div.table-group:nth-of-type(2) > .table-row");
List<String> arrayList = new ArrayList();
for (Element ele : elm) {
String event = ele.select("div.col:nth-of-type(2)").text();
String time = ele.select("div.col:nth-of-type(3)").text();
String port = ele.select("div.col:nth-of-type(4)").text();
String vessel = ele.select(".td_vesseltype.col").text();
Event ev = new Event();
System.out.println(event);
System.out.println(time);
System.out.println(port);
System.out.println(vessel);
}
}
}
sample of the div classes i want to scrape
<div style="box-sizing: border-box;padding: 0px 10px 10px 10px;">
<div class="cs-table">
<div class="heading">
<div class="col" style="width: 10px"></div>
<div class="col" style="width: 110px">Event</div>
<div class="col" style="width: 120px">Time (<span class="tooltip" title="My Time: In your current TimeZone">MT</span>)</div>
<div class="col" style="width: 150px">Port</div>
<div class="col">Vessel</div>
</div>
<div class="table-group">
<div class="table-row">
<div class="col"><i class="fa fa-sign-out red"></i></div>
<div class="col">Departure</div>
<div class="col" style="text-align: center;">2022-02-14 <b>16:51</b></div>
<div class="col"><img class="flag_line tooltip" src="/icons/flags2/16/GB.png" title=" United Kingdom"/>BELFAST</div>
<div class="col td_vesseltype"><img src="/icons/icon7_511.png"><span class="padding_18">WILSON BLYTH [GB]</span></div>
</div>
</div>
<div class="table-group">
<div class="table-row">
<div class="col"><i class="fa fa-flag-checkered green"></i></div>
<div class="col">Arrival</div>
<div class="col" style="text-align: center;">2022-02-14 <b>16:51</b></div>
<div class="col"><img class="flag_line tooltip" src="/icons/flags2/16/GB.png" title=" United Kingdom"/>HUNTERS QUAY</div>
<div class="col td_vesseltype"><img src="/icons/icon6_511.png"><span class="padding_18">SOUND OF SOAY [GB]</span></div>
</div>
</div>
<div class="table-group">
<div class="table-row">
<div class="col"><i class="fa fa-sign-out red"></i></div>
<div class="col">Departure</div>
<div class="col" style="text-align: center;">2022-02-14 <b>16:51</b></div>
<div class="col"><img class="flag_line tooltip" src="/icons/flags2/16/GB.png" title=" United Kingdom"/>LARGS</div>
<div class="col td_vesseltype"><img src="/icons/icon6_511.png"><span class="padding_18">LOCH SHIRA [GB]</span></div>
</div>
</div>
<div class="table-group">
<div class="table-row">
<div class="col"><i class="fa fa-sign-out red"></i></div>
<div class="col">Departure</div>
<div class="col" style="text-align: center;">2022-02-14 <b>16:51</b></div>
<div class="col"><img class="flag_line tooltip" src="/icons/flags2/16/GB.png" title=" United Kingdom"/>RYDE</div>
<div class="col td_vesseltype"><img src="/icons/icon4_511.png"><span class="padding_18">ISLAND FLYER [GB]</span></div>
</div>
</div>
You can start with looping over the table's rows: the selector for the table is .cs-table so you can get the table with Element table = doc.select(".cs-table").first();. Next you can get the table's rows with the selector div.table-row - Elements rows = doc.select("div.table-row"); now you can loop over all the rows and extract the data from each row. The code should look like:
Element table = doc.select(".cs-table").first();
Elements rows = doc.select("div.table-row");
for (Element row : rows) {
String event = row.select("div.col:nth-of-type(2)").text();
String time = row.select("div.col:nth-of-type(3)").text();
String port = row.select("div.col:nth-of-type(4)").text();
String vessel = row.select(".td_vesseltype.col").text();
System.out.println(event + "-" + time + " " + port + " " + vessel);
System.out.println("---------------------------");
// Do stuff with data here
}
Now it's up to you to decide if you want to keep the data in some array/list inside the loop and use it later, or to insert it directly to your database.
Recently I'm struggling in my job with an issue of creating a proper Xpath selector to get the content of the demanded text area.
Here's the structure of my HTML document:
<div id="X115OutsideBorder" style="left: 59.1%; top: 855px; width: 14.6%; height: 22px; position: absolute;">
<div class="CheckBox ltr mandatoryFieldStyle " id="X115Border">
<div class="xEdit FormatInputReadonly" id="X115Edit">
<div style="display: inline-block; direction: ltr;">
<label disabled="" class="xCheckboxIcon" id="X115Icon" for="X115"></label>
<input name="instance/CUST.risk.major.inc" tabindex="-32768" disabled=""
id="X115" onclick="handleOnClick(this, event);"
onfocus="handleOnFocus(this, event);" onblur="handleOnBlur(this, event);
applyToSameControl(this);" onchange="handleOnChange(this, event);"
type="checkbox" readonly="" value="true" datachangeevent="3034" dvdvar=""
buttonid="" sctype="CheckBox" ref="instance/CUST.risk.major.inc">
</div>
<span class="xCheckboxLabelSpan FormatLabel " id="X115LabelSpan">
<label class="xCheckboxLabel" id="X115Label" for="X115" enabledstyle="
color:
#000000;
">Risk to Major Incident?</label>
</span>
</div>
</div>
</div>
<input id="X117" type="hidden" value="0" dvdvar="instance/CUST.interface.error">
<span class="Label" id="X119" style="left: 0%; top: 885px; width: 15.1%; height: 22px; text-align: right; position: absolute;" type="label">
<label id="X120_Label" for="X120">Additional Information</label>
</span>
<div class="MultiText" id="X120Border" style="left: 16%; top: 885px; width: 71.5%; height: 187px; position: absolute;">
<div class=" mandatoryFieldStyle xEdit xTextArea" id="X120Edit" style="height: 187px;">
<textarea name="instance/Additional.information" tabindex="-32768" id="X120"
style="height: 185px;" onkeyup="
lockFormOnValueChange(this);
"
onclick="handleOnClick(this, event);" onfocus="handleOnFocus(this, event);"
onblur="handleOnBlur(this, event); applyToSameControl(this);"
onchange="handleOnChange(this, event);" rows="4" cols="81"
dvdvar="instance/Additional.information" buttonid="" sctype="MultiText">
</textarea>
</div>
</div>
So I created a class in Java with usage of a Selenium framework, looking as follows:
Thread.sleep(5000);
WebDriverWait wait = new WebDriverWait(driver, 15);
WebElement additionalInfoElement = wait.until
(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[#class='sm-clpsSectionCnt ie cntExpanded']
//div[#class='MultiText']//div[#class=' mandatoryFieldStyle xEdit xTextArea'] //textarea[contains(#id,'X120')]")));
driver.findElement(By.xpath("//div[#class='sm-clpsSectionCnt ie cntExpanded'] //div[#class='MultiText']//div[#class=' mandatoryFieldStyle xEdit xTextArea']
//textarea[contains(#id, 'X120')]"));
Thread.sleep(3000);
additionalInfoElement.click();
String additionalInfoContent = additionalInfoElement.getText();
driver.switchTo().defaultContent();
Map<String, String> assigneesToKeywords = new HashMap<>();
assigneesToKeywords.put("ee57977","Please select the affected Process / Application::SSR");
assigneesToKeywords.put("c308042","Please select the affected Process / Application::SSR1");
assigneesToKeywords.put("p853780","Please select the affected Process / Application::MMSR");
assigneesToKeywords.put("c323607","Please select the affected Process / Application::Reporting");
assigneesToKeywords.put("c152236","Please select the affected Process / Application::BOI");
assigneesToKeywords.put("ex73629","K3, K4");
assigneesToKeywords.put("C321274","Please select the affected Process / Application::CPR");
assigneesToKeywords.put("X","Please select the affected Process / Application::FOBO");
assigneesToKeywords.put("c325285","Please select the affected Process / Application::T-RECS");
for (Map.Entry<String,String> entry : assigneesToKeywords.entrySet()){
if (entry.getValue().contains(additionalInfoContent)){
chosenAssignee=entry.getKey();
}else
chosenAssignee="XXXX";
}
return chosenAssignee;
}
What I'm trying to do right now, is to get the content of the text area named "Additional Info" and compare it against the values of the map "assigneesToKeywords". However, Xpath which I created doesn't work and I ran out of ideas how to fix it... I still get the following error: "Expected condition failed: waiting for visibility of element located by By.xpath: ..."
So far I was trying to increase the amount of a time-out, but it didn't work. I guess it's all about the proper construction of an Xpath.
Any ideas of what I'm doing wrong? Thanks a lot in advance guys!
Best regards,
Mateusz.
As I'm understanding it you're trying to find the textarea that is labeled Additional Information. This label element has a #for attribute that is the #id attribute of the textarea element.
<label id="X120_Label" for="X120">Additional Information</label>
<textarea name="instance/Additional.information" tabindex="-32768" id="X120" ...</textarea>
Try this:
WebElement additionalInfoElement = driver.findElement(By.xpath("//textarea[#id=//label[text()='Additional Information']/#for]"));
String additionalInfoContent = additionalInfoElement.getText();
what i have tried:
Select listbox = new Select(
driver.findElement(By.xpath("//*[#id='multiselect_categories']"))
);
listbox.selectByValue("ATM");
Html code when some option choose:
<input name="multiselect_categories" id="multiselect_categories"
type="text" autocomplete="off" placeholder="Select option"
tabindex="0" class="multiselect__input" style="display: none;">
<div class="multiselect__tags">
<div class="multiselect__tags-wrap" style="">
<span class="multiselect__tag">
<span>Actions and Practices</span>
<i aria-hidden="true" tabindex="1" class="multiselect__tag-icon"></i>
</span>
<span class="multiselect__tag">
<span>Air Carrier Services and Safety Oversight</span>
<i aria-hidden="true" tabindex="1" class="multiselect__tag-icon"></i>
</span>
</div>
<div class="multiselect__spinner" style="display: none;"></div>
<input name="multiselect_categories" id="multiselect_categories"
type="text" autocomplete="off" placeholder="Select option"
tabindex="0" class="multiselect__input"
style="width: 0px; position: absolute; padding: 0px; display: none;">
</div>
<div class="multiselect__content-wrapper" style="max-height: 291.375px; display: none;">
<ul class="multiselect__content" style="display: inline-block;">
<li class="multiselect__element">
<span data-select="Press enter to select" data-selected="Selected"
data-deselect="Press enter to remove" class="multiselect__option">
<span>ATM</span>
</span>
</li>
<li class="multiselect__element">
<span data-select="Press enter to select" data-selected="Selected"
data-deselect="Press enter to remove" class="multiselect__option
multiselect__option--selected">
<span>Actions and Practices</span>
</span>
</li>
<li class="multiselect__element">
<span data-select="Press enter to select" data-selected="Selected"
data-deselect="Press enter to remove" class="multiselect__option
multiselect__option--selected">
<span>Air Carrier Services and Safety Oversight</span>
</span>
</li>
</ul>
</div>
CODE that failed when adding to selenium code when adding to testng:
#Test(description = "Test5")
public void chooseCatagory(String... catagories) {
for(String catagory: catagories) {
// input catagory in text box which display placeholder `Select option`
driver.findElement(By.cssSelector("div.multiselect__tags #multiselect_categories"))
.sendKeys(catagory);
// find the item from auto-suggest list
driver.findElement(By.cssSelector("div.multiselect__tags + div > ul"))
.findElement(By.xpath("./li//span[text()='"+catagory+"']"))
.click();
}
}
chooseCatagory("ATM", "Airports");
Error from the above code:
org.testng.TestNGException:
Cannot inject #Test annotated Method [chooseCatagory] with [class [Ljava.lang.String;].
For more information on native dependency injection please refer to http://testng.org/doc/documentation-main.html#native-dependency-injection
org.testng.TestNGException:
HTML when there is nothing chosen:
<input name="multiselect_categories" id="multiselect_categories"
type="text" autocomplete="off" placeholder="Select option" tabindex="0" class="multiselect__input" style="display: none;">
<span><span class="multiselect__single">
Select option
</span></span>
what the list contains:
ATM,Action, refer to screenshot
#Test(description = "Test5")
public test_chooseCatagory() {
chooseCatagory("ATM", "Airports");
}
private void chooseCatagory(String... catagories) {
for(String catagory: catagories) {
// click the down arrow at right to make the filter text box and
// all option list display
driver.findElement(By.cssSelector("div.multiselect__select"))
.click();
// input catagory into text box to filter matched options
driver.findElement(By.cssSelector(".multiselect__tags #multiselect_categories"))
.sendKeys(catagory);
// click the option from filtered option list
driver.findElement(By.cssSelector(".multiselect__content-wrapper > ul"))
.findElement(By.xpath("./li//span[text()='"+catagory+"']"))
.click();
// sleep 2 seconds before next choosing
try {
Thread.sleep(2000);
}
catch(Exception e) {
}
}
}
So I've read the Iterations part of the documentation already but still didn't give any idea on how to do the following:
Iterate per 2 records (since I am rendering something like below) and access list by index.
<div class="row">
<div class="col-md-6">
...
</div>
<div class="col-md-6">
...
</div>
</div>
Basically if this were in code, it looks something like
for (int i = 0; i < size;) {
// do stuff
// manual increment
if (i + 2 > size) {
i++;
} else {
i += 2;
}
}
Any other approach that would satisfy my problem is always welcome too!
I was able to solve it using something like below:
Basically, I still loop individually but just skip every other record using th:if="${stat.even}" and just get the next record by stat.index + 1.
Be really cautious about the IndexOutOfBoundsException though.
<div class="row" th:each="hivRisk, stat : ${hivRiskList}" th:if="${stat.even}">
<div class="col-md-6" th:with="leftRisk=${hivRiskList.get(stat.index)}">
<div class="checkbox checkbox-styled">
<label>
<input type="checkbox" value="-1" th:value="${leftRisk.id}"/>
<span th:text="${leftRisk.name}">HIV Risk</span>
</label>
</div>
</div>
<div class="col-md-6" th:if="${stat.index + 1 < hivRiskList.size()}" th:with="rightRisk=${hivRiskList.get(stat.index + 1)}">
<div class="checkbox checkbox-styled">
<label>
<input type="checkbox" value="-1" th:value="${rightRisk.id}"/>
<span th:text="${rightRisk.name}">HIV Risk</span>
</label>
</div>
</div>
</div>
I need to select a category from the below html.
I tried different options, and different ways showed in the blogs, but not able to select the options.
Any help will be appreciated.
One way I am using as (Not a better one)
private boolean select_dropdown_xpath(String value, String seleniumObjectValue) {
try {
boolean isListItemFound = false;
int i = 0;
do {
i++;
String category = driver.findElement(By.xpath(seleniumObjectValue+"/div["+ i +"]")).getText();
if(category.equals(value)) {
driver.findElement(By.xpath(seleniumObjectValue+"/div["+ i +"]")).click();
isListItemFound = true;
}
} while (isListItemFound == false);
if(!(isListItemFound)) {
return false;
}
} catch(Exception e) {
return false;
}
return true;
}
Tool: Selenium WebDriver 2.28
with Java
Thanks
Purna
HTML:
<div class="drop-down">
<div class="label_field">
<label>Category:</label>
<fieldset>
<div id="Ccategory" class="jSym_select_element jSym_pie jSym_noSelectText false hover" tabindex="0" textval="Default" style="width: 350px;">
<div class="jSym_drop_arrow false"/>
<div class="jSym_select_inner false">Default</div>
</div>
<div id="selectDrop" class="jSym_select_drop jSym_noSelectText " style="height: 50px; width: 350px; margin-top: 10px;">
<div class="jSym_select_item jSym_noSelectText" optionval="Default">Default</div>
<div class="jSym_select_item jSym_noSelectText" optionval="Reset">Reset</div>
</div>
<select id="select_category" class="jSym_dropdown" name="category" style="visibility: hidden;">
<option value="Default">Default</option>
<option value="Reset">Reset</option>
</select>
</fieldset>
</div>
</div>
Try this code:
Select sele = new Select(driver.findElement(By.id("select_category")));
//Select the dropdown by using the displayed value.
sele.selectByVisibleText(`displayed value`);
//or you can Select the dropdown by using the index value.
sele.selectByIndex(`index value`);
//or you can Select the dropdown by using the value attribute.
sele.selectByIndex(`value in the value attribute`);
In your case the dropdown visibility is hidden. So, first make it as visible by using JavaScript Executor class. Then use the above code.