I've got an issue trying to select items from a dropdown menu on a javascript webpage. My end goal is to fill in the menu value via a userform but I'm not having any success creating the VB to select the drop down. Web code is below
<select name="dateSelector0" class="clsInputArea selectBox valid" style="display: none; "onchange="setDateRange(this, 'rtf[0].val1', 'rtf[0].val2')">
<option value="-1"></option>
<option value="1">Last Month</option>
<option value="2">Current Month</option>
<option value="3">Next Month</option>
<option value="4">Last Year</option>
<option value="5">Current Year</option>
<option value="6">Next Year</option>
<option value="7">First Quarter</option>
<option value="8">Second Quarter</option>
<option value="9">Third Quarter</option>
<option value="10">Fourth Quarter </option></select>
<a tabindex="NaN" title="" class="selectBox clsInputArea selectBox-dropdown" style="width: 147px; display: inline-block;" href="javascript:void(0);"><span class="selectBox-label" style="width: 127px;"> </span><span class="selectBox-arrow"></span></a>
I've tried various GetElementsBy, with both the above name, trying the below with the name and with ID rtf[0].val1 but to no avail, example below. I believe going by the dateSelector0 name will be best, but I would greatly appreciate input from those of you better at this than me.
ie.Document.Body.GetElementsByname("dateSelector0").Value = "1"
ie.Document.Body.GetElementsByname("dateSelector0").Item(0).FireEvent ("onchange")
You almost had it. I just tried:
Sub IE_Navigate()
'Declare
Dim IE As Object
'Use IE
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
IE.navigate ("Website URL")
'Wait for Load to finish
While IE.readyState <> 4
DoEvents
Wend
Application.Wait (Now + TimeValue("0:00:01"))
Both of these approaches worked for me.
Select data from dropmenu by ClassName:
IE.document.getElementsByClassName("clsInputArea")(0).Value = 1
Or
Select data from dropmenu by Name
IE.document.getElementsByName("dateSelector0")(0).Value = 1
Which returned the first item from the drop down menu.
EDITED
If you observe the following CSS selector, where "." means class and " option" means select all "option" tags inside elements of that class (clsInputArea selectBox valid), you will see it makes the correct selections.
Note: You aren't allowed compound names in CSS selectors which is why the spaces are replaced with "." in the class name.
Sample CSS query results:
VBA:
Now, we can translate that into the following syntax:
ie.Document.getElementsByClassName("clsInputArea selectBox valid")(0).getElementsByTagName("option")
This assumes that index 0 is the correct one to use for elements of the class "select_list". You can easily inspect the collection to find the right index if you set it to a variable e.g.
Dim x As Object
Set x = ie.Document.getElementsByClassName("clsInputArea selectBox valid")(0).getElementsByTagName("option")
Assuming the object is not nothing then we can either select an option by its index (i.e. position) or by looping the collection and selecting when a certain condition is met e.g. its innerText matches a wanted phrase.
Selecting by Index:
This means for you you might say
x.SelectedIndex = 1 to select Last Month
Selecting by looping:
Dim currentOption As Object
For Each currentOption In x
If InStr(currentOption.innerText, "Last Month") > 0 Then
currentOption.Selected = True
Exit For
End If
Next currentOption
Full example of selecting drop down:
As you don't supply an URL, here is an example using a different website. The webpage target drop down, Código de Ativo, is shown here:
The target option to select is AALR21, which is number 3, or item 2 on 0 based index.
Looping and matching on the item innertext AAL21:
Option Explicit
Public Sub MakeSelectiong()
Dim IE As New InternetExplorer
Const URL = "http://www.debentures.com.br/exploreosnd/consultaadados/sndemumclique/"
Const optionText As String = "AALR21" 'Number2
Application.ScreenUpdating = False ' a.selectedIndex = 2
With IE
.Visible = True
.navigate URL
While .Busy Or .readyState < 4: DoEvents: Wend
Dim a As Object, currentOption As Object
Set a = .document.getElementById("ctl00_ddlAti")
For Each currentOption In a.getElementsByTagName("Option")
If InStr(currentOption.innerText, optionText) > 0 Then
currentOption.Selected = True
Exit For
End If
Next currentOption
Stop
.Quit
End With
End Sub
Selecting by index:
I could remove the entire loop and simply say:
a.selectedIndex = 2
Related
Use case is to select an item from a dropdown with help of selenium. I have tried the following but unable to achieve my goal. The input tag refers to a datalist .
String baseUrl = "https://angular-cwmwke.stackblitz.io" ;
driver.get(baseUrl); // opens the webpage
// Put some wait for the page to load the dropdown
driver.findElement(By.xpath("//input[#list='id-car']")).click(); // clicks on the dropdown
driver.findElement(By.xpath("//*[#id='id-car']/option[1]")).click(); // Does not works.
It fails and this is the exception .
JavascriptException: javascript error: option element is not in a select
Selenium version : 3.14.0 and
Chrome version : 78.0.3904.108
Here is the HTML ( Please ignore the option values ) :
<input _ngcontent-c0="" list="id-car" ng-reflect-model="[object Object]" class="ng-pristine ng-valid ng-touched">
<datalist _ngcontent-c0="" id="id-car">
<!--bindings={
"ng-reflect-ng-for-of": "[object Object],[object Object"
}-->
<option _ngcontent-c0="" value="[object Object]" ng-reflect-value="[object Object]">Ford-GTX</option>
<option _ngcontent-c0="" value="[object Object]" ng-reflect-value="[object Object]">Ferarri - Enzo</option>
<option _ngcontent-c0="" value="[object Object]" ng-reflect-value="[object Object]">VW - Amarok</option>
</datalist>
Some things to note initially about the datalist tag, this is different than a select in that a select element limits the choices to a predefined set, and a datalist gives suggested options, but can take any input. Also, the datalist options are usually used with the value attribute, so in this case (and you can test this manually too) when you click any option, the displayed value is the same ([object Object]).
That being noted, the reason you are getting an JavascriptException: javascript error: option element is not in a select is due to, I think, Selenium expecting option tags to be under a select element. The datalist tag operates more like a regular input text box than a select, and must be treated as such when using Selenium.
So, firstly, if you have access to that html code I would update the values to be what the text is now. It would look something like this:
<option _ngcontent-c0="" value="Ford-GTX" ng-reflect-value="Ford-GTX"/>
<option _ngcontent-c0="" value="Ferarri - Enzo" ng-reflect-value="Ferarri - Enzo"/>
<option _ngcontent-c0="" value="VW - Amarok" ng-reflect-value="VW - Amarok"/>
The Selenium code for "selecting" a certain option would then be something like this:
var input = driver.findElement(By.xpath("//input[#list='id-car']"));
var option = driver.findElement(By.xpath("//*[#id='id-car']/option[1]"));
var value = option.getAttribute("value");
input.clear();
input.SendKeys(value);
If this isn't the behavior you would like to achieve, and again you have access to the html, you might consider using a select element instead.
If you don't have access to the html, and really want to use the inner html text, you can simply modify the above code to reference the innerHTML instead of value:
var input = driver.findElement(By.xpath("//input[#list='id-car']"));
var option = driver.findElement(By.xpath("//*[#id='id-car']/option[1]"));
var text = option.getAttribute("innerHTML");
input.clear();
input.SendKeys(text);
Keep in mind however, that this might lead to some strange behavior upon form submit, as a true user click is displaying the value, and not the text.
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.
I understand how to Select an element from dropdown using selectByIndex method. Howeverwhen I try to perform selectByIndex ( select.selectByIndex (index) ) on the following HTML snippet:
<select id="destinationAllocationId" name="destinationAllocationId">
<optgroup label="Anywhere Virtual Cards">
<option value="1555555555f0a19">NNN0 [*-2453]</option>
<option value="1555555555f0519">NNN1 [*-8354]</option>
<option value="155555555550419">NNN2 [*-5765]</option>
<option value="155555555520919">NNN3 [*-8875]</option>
</optgroup>
</select>
</div>
I'm unable to maintain the selected option.
At first it appears to work (the dropdown menu opens and the correct option is highlighted inblue). However as soon as the program continues with execution (something unrelated to dropdown) the dro pdown reverts back to the original state (the default option).
In other words, initially the the program selects the appropriate option just fine but it automatically reverts back to the default option and doesn't actually change the value in the dropdown once the dropdown menu disappears (Usually when I select an option manually I can see that option being selected once the drop down is closed however in this case as long as dropdown is open the option is selected but as soon as it closes the default option is selected)
Any ideas why this happens
i had this issue, too
this is how i solved it
/**
* helper method for handling drop downs
* #param idPrefix the id of the drop down without any _xy
* #param value the value to be selected
*/
public void clickDropdown(String idPrefix, String value) {
// find & click on the label element of the drop down
firefoxDriver.findElement(By.id(idPrefix + "_label")).click();
// find & choose element from drop down
firefoxDriver.findElement(By.xpath("//div[#id='" + idPrefix + "_panel']/div/ul/li[text()='" + value + "']")).click();
}
u call it like this:
// choosing an element in the drop down menu
clickDropdown("projectForm:stellvertreter", "StringToSelect");
this i wrote for use with primefaces, you have to modify it anyway but u get the idea
for the issue with selecting and reverting... try sending an enter key press to the selected item or clicking it again... didnt have that with my approach
I'm trying to create the test cases using Selenium WebDriver with Java. I have the following HTML syntax in the source.
<label for="00N30000005wfev"><span class=class="requiredMark">*</span>Type</label>
<select id="00N30000005wfev" tabindex="34" name="00N30000005wfev">
<option value="Account">Account</option>
<option value="Client">Client</option>
<option value="Service">Service</option>
</select>
All the "for","id" and "name" value are dynamically generated when the application creates a new item every time. The label name is fixed for the item details. How can I dynamically retrieve this value based on the label name value (e.g. Type)?
When Java runs, it will look at the "Type" label first, then it will be able to find the "for" value.
Thanks
You could use a XPath expression to do this:
WebElement element = driver.findElement(By.xpath("//label[contains(text(),'Type')]"));
String labelForValue = element.getAttribute("for");
I'm going to say the dreaded words - i'm fairly new to java
But I can't find my answer online in the most obvious places so I'm going to ask the question here
The program i'm testing is a customer database, when you select the Country drop down box, other fields may become mandatory
Amongst those fields is "State"
This field can either be a free text or a drop down box
So i've created an if statement that lets me input free text if the conditions for a state drop down box is not met:
if (selenium.isElementPresent("xpath=//*[#id='state']/option[2]"))
{selenium.select("xpath=//select[#id='state']/option","index=2" );}
else {checkfield("xpath=//td[contains(.,'State/Province:')]/
preceding-sibling::td[contains(.,'*')]",
"xpath=//*[#id='address.state']",
state1);}
<td>
<!-- begin state drop down menu -->
<select id="state" name="address.state">
<option selected="selected" value="">Please Select</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="WY">Wyoming</option>
</select>
<!-- end state drop down menu -->
</td>
My question may be a very basic one and you guys can probably already see where my code fails
I want to choose option 2 of the drop down box if a drop down box is present, otherwise enter a string which has been declared in the free text box
up until now i've only ever used selenium.select for selecting a label which has specific text
Firstly: Upgrade to Selenium WebDriver
Selenium 1.0 is just an undead.
That's the way to do it with WebDriver:
if(stateDropdown.isEnabled()&&(!stateField.isEnabled())){
Select state = new Select(stateDropdown);
// state.selectByValue("Illinois");
state.selectByIndex(2);
}else if(stateField.isEnabled()&&(!stateDropdown.isEnabled){
stateField.sendKeys("Salzburg");
}
Isn't it pretty?
Could you try doing it using labels instead?
selenium.select("state", "label=Alaska")
You're likely to know what the values will be unless new states get added (seems unlikely)