Select item in angular selectbox using selenium - c#

I am trying to make a selection in a dropdown using selenium.
The selectbox is not a html type of 'select' but is using angular so it is a 'mat-select' html-tag.
I simplified my code to get this to work but could not get it to work, this is my code right now:
var q = driver.FindElement(By.TagName("mat-select"));
new SelectElement(q).SelectByText("My List Value");
When I run this code I get:
Element should have been select but was mat-select
How do I solve this? I tried using SendKeys but since "My List Value" has spaces, the space triggers the selectbox to open or close and it won't select the correct value. Then I read about SelectByText but that seems to require a normal '' but I only have a ''.
Here is the html of the select:
<div class="mat-form-field-infix">
<mat-select _ngcontent-qpv-c46="" class="mat-select ng-tns-c12-118 ng-pristine ng-valid mat-select-empty ng-star-inserted ng-touched" role="listbox" id="mat-select-5" tabindex="0" aria-labelledby="mat-form-field-label-41" aria-required="false" aria-disabled="false" aria-invalid="false" aria-multiselectable="false">
<div class="mat-select-trigger" aria-hidden="true" cdk-overlay-origin="">
<div class="mat-select-value">
<!---->
<span class="mat-select-placeholder ng-tns-c12-118 ng-star-inserted"> </span>
<!---->
</div>
<div class="mat-select-arrow-wrapper">
<div class="mat-select-arrow"></div>
</div>
</div>
<!---->
</mat-select>
<span class="mat-form-field-label-wrapper">
<!---->
<label class="mat-form-field-label ng-tns-c24-117 mat-empty mat-form-field-empty ng-star-inserted" id="mat-form-field-label-41" for="mat-select-5" aria-owns="mat-select-5">
<!----><!---->
<mat-label _ngcontent-qpv-c46="" class="ng-star-inserted">Properties</mat-label>
<!----><!---->
</label>
</span>
</div>
All the options are missing in the html? I know c# but not angular so this looks a bit funny to me. Have not tried to click because I don't know how to find them when I can't even see them myself.
When I click on the select all items appear in some magical angular way...

The reason you got the error is because SelectElement() can only be used with an HTML SELECT element. The mat-select (and some other elements) may be formatted to look like a dropdown but they are not SELECT dropdown elements. Because of this, we aren't going to be able to use SelectElement() but there are ways around this.
The simplest way I've found is to find the mat-select element (typically by ID) and click it to open the dropdown. Then click the desired option using an XPath that contains the expected string. The code below shows this but the second locator is a guess because the HTML you provided didn't show the dropdown options. If you update the HTML to show this, I can update and test the locator but even if you don't, this should point you in the right direction.
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("mat-select-5"))).Click();
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath("//span[contains(text(),'My List Value')]"))).Click();
I added waits just to be safe. They may not be needed.
driver.FindElement(By.Id("mat-select-5")).Click();
driver.FindElement(By.XPath("//span[contains(text(),'My List Value')]")).Click();
I'm assuming you are going to use this more than once. In that case, I would write a method that takes in the desired option and selects it.
public void SelectProperty(string propertyName)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.ElementToBeClickable(By.Id("mat-select-5"))).Click();
wait.Until(ExpectedConditions.ElementToBeClickable(By.XPath($"//span[contains(text(),'{propertyName}')]"))).Click();
}
and call it like...
SelectProperty("My List Value");

Selenium doesn't provide methods for interacting with mat-select. You need to write a custom program for Selenium to interact with a mat-select.
Here is an example of how to select a mat-select option with Selenium using C#:
IWebElement field = driver.FindElement(By.cssSelector('mat-select'));
// Click to open the dropdown.
field.Click();
// Query for options in the DOM. These exist outside of the mat-select component.
IReadOnlyList<WebElement> options = driver.FindElements(By.cssSelector("mat-option"));
// Find the option with the text that matches the one you are looking for.
options.First(element => element.GetText() == "My List Value")
// Click it to select it.
.Click();
You will probably want to wrap this up in a re-usable method of some sort.

Related

How to sendkeys to a <p> tag through C# and Selenium

i want to sendkeys "description" within a textarea. I have tried all the possible ways but does not work.
HTML of the element :
<div class="ta-scroll-window ng-scope ta-text ta-editor form-control" ng-hide="showHtml">
<div class="popover fade bottom" style="max-width: none; width: 305px;">
<div class="arrow"></div>
<div class="popover-content"></div>
</div>
<div class="ta-resizer-handle-overlay">
<div class="ta-resizer-handle-background"></div>
<div class="ta-resizer-handle-corner ta-resizer-handle-corner-tl"></div>
<div class="ta-resizer-handle-corner ta-resizer-handle-corner-tr"></div>
<div class="ta-resizer-handle-corner ta-resizer-handle-corner-bl"></div>
<div class="ta-resizer-handle-corner ta-resizer-handle-corner-br"></div>
<div class="ta-resizer-handle-info"></div>
</div>
<div id="taTextElement737852736512107" contenteditable="true" ta-bind="ta-bind" ng-model="html" ta-keep-styles="true" class="ng-pristine ng-valid ta-bind ng-empty ng-touched" an-form-object-name="Açıklama" name="Açıklama">
<p>
<br>
</p>
</div>
</div>
Code trial :
Dim action2 = New Actions(driver)
Dim cekbul2 = driver.FindElement(By.XPath("//*#id=""taHtmlElement737852736512107""]"))
cekbul2.SendKeys("Açıklama")
Console.Write("textarea send description")
or
Dim cekbul2 = driver.FindElement(By.XPath("//textarea[#class='ng-pristine ng-untouched ng-valid ng-scope ta-bind ta-html ta-editor form-control ng-empty ng-hide' and #id='taHtmlElement737852736512107']"))
The error is :
"no such element: Unable to locate element does not work" give error
Your html does not have a text area input field inside it.
When you use an xPath that says
'//textarea' this means that you are looking for an element that has tags of <textarea> </textarea>
It looks like your html is actually div's that are styled up to look like text areas.
That is why your second attempt will never work - because you are looking for a textarea where none exists.
Typically, in the situation where a div is styled up to work like a text area or textbox, you will find that the div has a backing input behind it.
These must be located between the
<form> and </form> tags in the html - otherwise the server would never be able to receive the data. (Html 5 provides new ways of working with this - but that is another story)
Can you examine your full html, and see if you can find the actual text area objects or the input type objects that end up containing the text content.
Type some dummy text, and use an html inspector tool within chrome or firefox to look for your dummy text.
If however, the post is completed by javascript - you may find that the javascript does not use inputs or text areas for containing the text and instead posts it external to any form elements. This is common with richtext emulators such as forum post pages.
If that is the case- you may need to experiment and find the appropriate html element that you need to send keys to in order for the content to work.
Also - could you try
Dim cekbul2 = driver.FindElement(By.XPath("//div[#id='taHtmlElement737852736512107']"))
I couldnt help but notice it had an xPath syntax error - you had no starting [ square bracket ] - also, in programming it is sometimes considered lazy a bad practice to wildcard / work with dynamics. I recommend always using the tag type for your xpaths, as opposed to '//*'
Worse case scenario, I would say that you could probably get around this by using Javascript execution. Eg: Directly setting the text, instead of 'sending the key strokes'.
However, this does not emulate human behavior - but it may be a necessary evil depending on your situation.
To send text to the <p> tag you have to use the ExecuteScript() method from IJavaScriptExecutor Interface and you can use the following code block :
((IJavaScriptExecutor)driver).ExecuteScript("document.getElementsByTagName("p")[0].innerHTML="Hasan Sarıkaya";");
I want to highlight some points here
Most probably your locator which you are using is not correct.
There are three way which I know to enter text using selenium
1)Use driver.findElement(yourLoator).sendKeys("Stringvalue");
2)You can use action class to send keys
3)You can use javascript executor to change innerHtml code
Personally ill not prefer the third solution, because we are testers I believe changing dom attribute is a good practice
Hope this will give you some help. please Let me know in case any query.

StaleElementException when Clicking on a TableRow in an Angular WebPage

<div id="crm" class="row gridrow clickable ng-scope" ng-repeat="customer in customerList" ng-click="gotoRecord(customer.id)">
<i class="col m1 s1 tiny fa fa-male"></i>
<div class="col m3 s11 ng-binding"> Allard</div>
<div class="col m2 s12 ng-binding"></div>
</div>
I have this snippet of HTML, it's one row displaying as a result of a search action for a Customer with nameCustomer 'Allard'. I want to click on this customer to continue to the next page but most of the time this results in a StaleElementException.
I tried it two different ways, using Protractor and without Protractor.
First way:
IWebElement elem = driver.FindElement(By.XPath("//*[contains(text(),'" + nameCustomer + "')]//parent::div[contains(#id,'crm')]"));
ExplicitWait.WaitAndClick(driver, elem);
Second way:
var customers = driver.FindElements(NgBy.Repeater("customer in customerList"));
foreach (var customer in customers)
{
if (elem.Text.Equals(nameCustomer))
{
elem.Click();
}
}
The problem (I think)
With StaleReferenceExceptions, an IWebElement that was previously created is no longer attached to the DOM (you probably already know this). What's most likely happening is this:
1: You click search.
2: Selenium executes driver.FindElement(...) and finds a matching element.
3: Then the search function finishes and the DOM updates. The old IWebElement found previously is gone.
4: Then Selenium tries to click on the element (which no longer exists, causing the StaleElementException. There is an element that matches the one that was there before, but it's not the same element in Selenium's eyes.)
Your statement that this happens "most of the time" makes me suspect this is the case even more, because the exception would depend on the order of events, which would vary depending on the relative speeds of Selenium vs. the web-page.
How to resolve (if this is your problem)
You need to find something on the page that will indicate to Selenium that the search action is done. This is where the creativity of writing GUI automation scripts comes in. If there is something on the page that you know will change as a result of the load, craft an explicit wait to ensure that is complete. Maybe there is a loading bar that shows up, or a message that appears when the search is done. You could grab an element that doesn't match your search before clicking search, then do an explicit wait to make sure it disappears before going on to look for the result you do expect to be there.
It would look something like this below.
# Search action performed before this.
WebDriverWait wait= new WebDriverWait(driver, TimeSpan.FromSeconds(secondsToWait));
wait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.XPath( expressionForElementThatWillDissapear )));
IWebElement elem = driver.FindElement(By.XPath("//*[contains(text(),'" + nameCustomer + "')]//parent::div[contains(#id,'crm')]"));
ExplicitWait.WaitAndClick(driver, elem);
I'd recommend making the method to implement the explicit wait above. These situations will come up often.

C# Selenium Button in a Span Class XPath (Amazon.com)

I am trying to create a proper XPATH syntax in C# to click on a download button from the Amazon business website. Everything I have tried is unable to find the button. Here are some of the things I've tried:
driver.FindElement(By.XPath("//button[#type='submit']")).Submit();
driver.FindElement(By.XPath("//span[contains(#class,'a-button-inner')][contains(text(),'downloadCSV_button-announce')]")).Submit();
driver.FindElement(By.XPath("//span[contains(#class,'a-button-inner')][contains(text(),'Download CSV')]")).Submit();
Below is the source code from the Amazon page. Can anyone help me to design the proper XPATH query to click this download button? Thank you.
<h1>Amazon Business Analytics</h1>
<div class="a-row a-spacing-medium a-grid-vertical-align a-grid-center">
<div class="a-column a-span12">
<span class="a-declarative" data-action="aba:download-csv" data-aba:download-csv="{}">
<span id="downloadCSV_button" class="a-button aok-float-right"><span class="a-button-inner"><input class="a-button-input" type="submit" aria-labelledby="downloadCSV_button-announce"><span id="downloadCSV_button-announce" class="a-button-text" aria-hidden="true">Download CSV</span></span></span>
</span>
You should try using WebElement#click() to perform click on element instead as below :-
driver.FindElement(By.CssSelector("input.a-button-input[aria-labelledby = 'downloadCSV_button-announce']")).Click();
Or if span element is clickable try as :-
driver.FindElement(By.Id("downloadCSV_button-announce")).Click();
Or
driver.FindElement(By.Id("downloadCSV_button")).Click();

Verifying child elements under a specific class in Selenium Web Driver (Code in C#)

I have an Options field in my website where there are two child options. One is Change Password and other is Logout. I want to verify whether this two options are available there in a sequential order. Which mean the first option should be Change Password and the second option should be Logout. Here is the HTML of that portion:
<div class="user-options-container">
<div class="user-options-header">OPTIONS</div>
<div class="user-options">
<div class="user-options-item" action="changepwd">Change Password</div>
<div class="user-options-item" action="logout">Logout</div>
</div>
</div>
How can I achieve that? What I know is I can get them creating a list of IWebElement class and then verify those elements. But, getting confused on how to do that. I am using Selenium Web Driver 2 and C# as my language.
Giving a quick solution on Java. But similar apis should be there in C#.
How about doing like this. For the HTML:
<div class="user-options-item" action="changepwd">Change Password</div>
<div class="user-options-item" action="logout">Logout</div>
get both the WebElement like this:
List<WebElement> elements = driver.findByElements(By.class("user-options-item"))
then you can get the text of element like:
elements.get(0).getText() //Change Password
elements.get(1).getText() //Logout
And check their orders. This should work for the given code, but if the same class is shared for other elements too, then the order will change.

Write query to parse HTML DOCUMENT with HtmlAgilityPack

I want to get the A href of that element in span class="floatClear" whose rating is minimum in
span class="star-img stars_4"
How can I use HtmlAgilityPack to achieve this behaviour I have give the html source of my file
<div class="businessresult"> //will repeat
<div class="rightcol">
<div class="rating">
<span class="star-img stars_4">
<img height="325" width="84" src="http://media1.px" alt="4.0 star rating" **title**="4.0 star rating">
</span>
</div>
</div>
<span class="floatClear">
<a class="ybtn btn-y-s" href="/writeareview/biz/KaBw8UEm8u6war_loc%NY">
</span>
</div>
The query I have written
var lowestreview =
from main in htmlDoc.DocumentNode.SelectNodes("//div[#class='rightcol']")
from rating in htmlDoc.DocumentNode.SelectNodes("//div[#class='rating']")
from ratingspan in htmlDoc.DocumentNode.SelectNodes("//span[#class='star-img stars_4']")
from floatClear in htmlDoc.DocumentNode.SelectNodes("//span[#class='floatClear']")
select new { Rate = ratingspan.InnerText, AHref = floatClear.InnerHtml };
But I do not know how to apply condition here at last line of LINQ query!
Don't select "rating" from the entire htmlDoc, select it from the previously found "main".
I guess you need something like:
var lowestreview =
from main in htmlDoc.DocumentNode.SelectNodes("//div[#class='rightcol']")
from rating in main.SelectNodes("//div[#class='rating']")
from ratingspan in rating.SelectNodes("//span[#class='star-img stars_4']")
from floatClear in ratingspan.SelectNodes("//span[#class='floatClear']")
select new { Rate = ratingspan.InnerText, AHref = floatClear.InnerHtml };
I hope it will not crash if some of those divs ans spans are not present: a previous version of the HtmlAgilityPack returned null instead of an empty list when the SelectNodes didn't find anything.
EDIT
You probably also need to change the "xpath query" for the inner selects: change the "//" into ".//" (extra . at the beginning) to signal that you really want a subnode. If the AgilityPack works the same as regular XML-XPath (I'm not 100% sure) then a "//" at the beginning will search from the root of the document, even if you specify it from a subnode. A ".//" will always search from the node you are searching from.
A main.SelectNodes("//div[#class='rating']") will (probably) also find <div class="rating">s outside the <div class="rightcol"> you found in the previous line.
A main.SelectNodes(".//div[#class='rating']") should fix that.

Categories

Resources