Select an option in a dynamic select with codeception? - codeception

<select>
<option value=''>-- Select an Option --</option>
#foreach ($options as $option)
<option value='{{ $option->value }}'>{{ $option->name }}</option>
#endforeach
</select>
Select the first dynamic option

$option = $I->grabTextFrom('select option:nth-child(2)');
$I->selectOption("select", $option);
$I->click("Submit");

I've run into the same issue quite often when starting out with Codeception. Using the recommended answer, I created a helper function in my AcceptanceTester class to make this a little easier.
public function selectFromDropdown($selector, $n)
{
$option = $this->grabTextFrom($selector . ' option:nth-child(' . $n . ')');
$this->selectOption($selector, $option);
}
Where $n is the position in the option list.
Then all you have to do is call it like this:
$I->selectFromDropdown('select', 1);
This has been working for me on pages that have several select's that load their option list based on the selected option of the previous select.

sorry i do not have submit button, in my case i have to select the dropdown element and somehow need to tell the codeception to finish the selection.At the moment i can select but that select is not visible as i suppose the selection is not finished somehow.Below is my code to select the element.
$I->selectOption('//*[#class="ng-scope" and #ng-controller="dataIsland"]/*[local- name()="select"]','partlycloudy');

Related

Select by attribute in Selenium

Is there a way to select by attribute? We have select drop down with incomplete visible text. We have to rely only on title. Is there a way to select by attribute?
My HTML looks something like
<select id="some ID" name="some Name" class="some class"
<option value="random number" title="testing pvt ltd">testing...</option>
<option value="random number" title="selenium HQ documentation">testing...
</option>
<option value="random number" title="selenium HQ API Request">selenium HQ...
</option>
Not natively, but nothing prevents you from implementing that method yourself, should you need it.
Something like this in C# should do the trick. Translation in your favourite language shouldn't be too hard.
public IWebElement GetWebElementByAttributeValue(string tagType, string attributeName, string attributeValue)
{
//finds all tags of a type, for example h1,a,etc...
//Here Driver is my instance of WebDriver
var allTags = Driver.FindElements(By.XPath("//" + tagType));
//iterate over all elements of that tag, and find the one whose attribute value you want
foreach (var v in allTags)
{
if ((v.GetAttribute.getText().equals(attributeName))
return v;
}
return null;
}
for you you would call it like that :
WebElement chosenOption = GetWebElementByAttributeValue("option","title","testing pvt ltd");
Hope this helps !
Naturally - the locators will go after the specific attribute and value.
Here are samples based on your source; xpath:
//select/option[#title="testing pvt ltd"]
If you're not familiar with xpath, it reads - select the option element that is a direct descendant (e.g. a child) of a select one, and has an attribute title (note the # char, which denotes thst an attribute name follows) with that value.
Absolutely the same with CSS:
select>option[title="testing pvt ltd"]
In general in CSS you can skip the double quotes around the value, if there's no whitespace in it - not like in your case, they are needed here.
el("input[value*='Edit'][type='submit']")
works without issue. I am using Fluentlenium and it makes thing a whole lot easier.
Use selectByValue method under Select class.

Is there any way to Locate the XPATH of the dropdown list Option using the name "test1","first_test","i2","i3" as mentioned in the code below

Is there any way to Locate the XPATH location of the dropdown list Option using the text "test1","first_test","i2","i3" as mentioned in the code below.
<select id="listid_select" class="select-box" style="width:100px;" name="list_id">
<option value="">NONE</option>
<option value="1">test1</option>
<option value="3">first_test</option>
<option value="6">i2</option>
<option value="7">i3</option>
<option value="8">i4</option>
<option value="9">i5</option>
<option value="10">i6</option>
<option value="11">i7</option>
<option value="12">i8</option>
<option value="13">i9</option>
<option value="14">Clone1</option>
i need to locate the option based on the "text name" instead of using "values", because there are values which goes on till 300 and more. It would be easy if i find out the option using the names.
Thanks in Advance :)
Selenium has Select functionality, which allows you to select by text or value. This is a c# example:
IWebElement element = driver.FindElement(By.XPath("//select[#id='listid_select']"));
SelectElement select = new SelectElement(element);
select.SelectByText("i2");
Richard's answer is the most correct, but if you want to do it just using XPath, you can. Note that this is one area where webdriver does not mimic user behaviour completely, you do NOT have to click on the select element and then the option element, just clicking on the option element will suffice.
Also a C# example:
IWebElement element = driver.FindElement(By.XPath("//select[#id='listid_select']/option[text()='i2']"));
element.Click();
You may be able to do it like this, using Java :
import org.openqa.selenium.support.ui.Select;
....
public void selectByString( String str ) {
try {
Select( driver.findElement( By.id("listid_select") )
.selectByVisibleText( str );
return true;
} catch ( Exception e ) {
return false;
}
}
...
boolean selected = selectByString( "test1");

Best approach to use when values are dynamically created?

I'm looking for some advice on how to select a WebElement.
When I click a button that creates a new 'page', the given value is dynamic, in that it is totally random and not sequential. I would expect it to follow something like: page 1 = 0, page 2 = 1, page 3 = 2, etc.
By default I begin with a page, and its value is c3. Although when I trigger a button to create new pages the values appear as follows:
<select class="j-currentPage f-feature-A" name="currentPage"> <option value="c3">New Page</option> <option value="c277">New Page 2</option> <option value="c383">New Page 3</option> <option selected="" value="c461">New Page 4</option> </select>
So when I attempt to use the following:
List<WebElement> option = chrome.findElements(By.tagName("option"));
option.get(0).click();
option.get(1).click();
option.get(0).click();
It's obviously failing as the values are c277, c346 etc etc, and there is no way of knowing what the values will be in a new session as they will always be different.
The stacktrace is throwing a StaleElementReferenceException because its not in the DOM.
How can I select each page when the values are completely random?
I'm using Webdriver and JUnit4.
EDIT
Test method: to create some new pages.
#Test
public void createANewPage(){
WebDriverWait wait = new WebDriverWait(chrome, 60);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("select.j-currentPage.f-feature-A[name=\"currentPage\"]")));
WebElement newPage = chrome.findElement(By.cssSelector("span.j-addViewBtn.ss-layers"));
newPage.click();
List<WebElement> option = chrome.findElements(By.tagName("option"));
option.get(1).click();
option.get(0).click();
}
Then a few tests later I want to call back the created page(s)
#Test
public void goToPage(){
new WebDriverWait(chrome, 60).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("select.j-currentPage.f-feature-A[name=\"currentPage\"]")));
List<WebElement> option = chrome.findElements(By.tagName("option"));
option.get(0).click();
option.get(1).click();
option.get(0).click();
}
I receive a org.openqa.selenium.StaleElementReferenceException at the point of the third option.get(0).click();
However I feel as though I'm approaching this the wrong way as the values are dynamically created <option value="c3">New Page</option>
<option value="c207">New Page 2</option>
<option value="c288">New Page 3</option>
<option selected="" value="c366">New Page 4</option>
The StaleElementReferenceException does not mean that it is not in the DOM. It means that the DOM has changed since you found that object and therefore may be deleted or changed. The change coudld be due to the page reloading or HTML refreshed via javascript. Either way, Webdriver throws the error because it thinks it is probably good idea to refind objects as the page has changed.
If you refind the elements, then it will work. You are finding by tag name, so the fact that ids have changed, should have no impact.
The following would work but does have performance issues;
chrome.findElements(By.tagName("option")).get(0).click
chrome.findElements(By.tagName("option")).get(1).click
chrome.findElements(By.tagName("option")).get(2).click
There will a better way to approach your problem for your particular application but hopefully understanding what stale elements actually indicates, will help you solve your issue.
None of the FindElement() strategies support using regular expressions for finding elements.
Though there is another way using regex, might help you
List<WebElement> option = chrome.findElements(By.tagName("option"));
now loop on the options and getText. Then do the regex check programmetically
Below is a demo code will not run. Please transfer it to jave
foreach(WebElement link in links)
{
string text = link.Text;
if (Regex.Match("your Regex here", text))
{
matchingLinks.Add(text);
}
}
foreach(string linkText in matchingLinks) // now you have the texts even if they are dynamics
{
WebElement element = driver.findElement(By.LinkText(linkText));
element.Click();
// do stuff on the page navigated to
driver.Navigate().Back();
}
OKay, I've just managed to solve the issue with the help of the contributors to this question.
It seems as though I just needed to use xpath instead of List<WebElement> so the below code now selects the pages I want.
#Test
public void jGoToPage(){
new WebDriverWait(chrome, 60).until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("select.j-currentPage.f-feature-A[name=\"currentPage\"]")));
WebElement page1 = chrome.findElementByXPath("//option[contains(.,'New Page')]");
page1.click();
WebElement page2 = chrome.findElementByXPath("//option[contains(.,'New Page 2')]");
page2.click();
WebElement selectPage1Again = chrome.findElementByXPath("//option[contains(.,'New Page')]");
selectPage1Again.click();
Perhaps not the best code, but if anyone would like to suggest a better way to refine it, please comment so others can learn.
The reason I declared New Page twice was because I was receiving a staleElementReferenceException again. So by simply attempting to perform page1.click(); immediately after page2.click(); is when that happened which is why I declared it as selectPage1Again
I also used this question to help with the solution.

Selenium 2 - getting the selected option from drop down list

<select class="selectCity">
<option></option>
<option value="Paris">Paris</option>
<option>New York</option>
<option>London</option>
</select>
Select op1 = new Select(driver.findElement(By.xpath("(//*[#id='cityTable']//*[contains(#class,'selectCity')])")));
List<WebElement> allSelectedOptions = op1.getAllSelectedOptions();
WebElement firstSelectedOption = op1.getFirstSelectedOption();
System.out.println("op1!!!!!"+firstSelectedOption.getText());
The user selected option on the web page is London.
Put the output is op1!!!!!
How to find the option that has been selected on the web page?
Thanks in advance!
Selenium.getSelectedLabel("//string locator");
The above code helps in knowing the option witch is currently selected and visible from the drop down list.
String locator of drop down box can by anything eg:- name,id,xpath
EG : Selenium.getSelectedLabel("name=productIdxSel");

selenium webdriver select element

I have a select control on my site. I am using page objects to interact with the page. If I do (with the first 2 lines under my class and the selectByValue in my method)
#FindBy(id="foo")
private Select foo;
foo.selectByValue("myValue");
It fails with a null pointer. I also tried without the #FindBy.
Now if I do this in my method it all works fine and selects the correct item
Select foo = new Select(sDriver.findElement(By.id("foo")));
foo.selectByValue("myValue");
Here is the actual web snippet for that control (edited to protect the innocent)
<select id="foo" name="service_name">
<option selected="selected" value="one">one</option>
<option value="two">two</option>
<option value="three">three</option>
</select>
Let me say that I have a work around for my issue but I don't get why the "normal" path is not working.
Thats because the Select class has this constructor:
Select(WebElement element)
See the Javadoc
So if you do something like this:
#FindBy(id="foo")
private WebElement wannabeSelect;
Select realSelect = new Select(wannabeSelect);
realSelect.selectByValue("myValue");
It should work.
BTW, I am using the same approach as you in the "workaround" because I dont wanna cast new WebElement object when I need Select object. But anyways, the
sDriver.findElement(By.id("foo"));
returns WebElement, so thats why its working. You can also do this:
WebElement wannabeSelect = sDriver.findElement(By.id("foo"));
Select foo = new Select(wannabeSelect);
There are two ways to select the option value:
One:
// Denotes option value - technical name
select.selectByValue(fieldValue);
Two:
// Denotes option text that is actually visible to be selected
select.selectByVisibleText(fieldValue);
Other way I achieved this is by using below method for all my onchange dropdownselection boxes. Pass id and selection and it works
public void onchangedropdownselection(String object, String value) {
WebElement obj = driver.findElement(By.id(object));
obj.sendKeys(value);
obj.sendKeys(Keys.UP);
obj.sendKeys(Keys.DOWN);
}
By doing up and down we are initialzing the script onchange.......