C# Selenium Calendar click - c#

Hope everyone is well. I am having difficulties with the clicking a calendar date.
Basically I click on a button a calendar pops up and when i inspect (google chrome) the date the following td is what I need to click;
<td class="dxeCalendarDay" savedcursor="[object Object]" style="cursor: pointer;">1</td>
I tried the following code but gave me an error;
FromCalendar = Chromedriver.FindElement(By.Id("ctl00_MainContent_dpStart_DDD_C_mt"));
IWebElement FromCalendar1 = Chromedriver.FindElement(By.XPath("//tr/td[contains(text(), '>1<'")); //
FromCalendar1.Click();
The error:
OpenQA.Selenium.InvalidSelectorException: 'invalid selector: Unable to locate an element with the xpath expression //ttr/td[contains(text(), '>1<' because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//ttr/td[contains(text(), '>1<'' is not a valid XPath expression.
Hope someone can help me. Thank you in advance.

The problem with your XPath is that you are trying to locate the element by contained text of >1<. That element doesn't contain >1<, it only contains 1. Change it to //td[#class='dxeCalendarDay'][.='1'] and it should work.
Looking at your code, it looks like you have other issues.
Your first line doesn't do anything... I'm assuming you meant to click to open the calendar?
Chromedriver.FindElement(By.Id("ctl00_MainContent_dpStart_DDD_C_mt")).Click();
In general, if you aren't going to reuse the variable, don't bother storing a reference to the web element. Change
IWebElement FromCalendar1 = Chromedriver.FindElement(By.XPath("//tr/td[contains(text(), '>1<'")); //
FromCalendar1.Click();
to just
Chromedriver.FindElement(By.XPath("//tr/td[contains(text(), '>1<'")).Click();
Since you are clicking an element and a calendar opens, you should assume that it might not be instantly available so you should add a wait. Your final code would look something like this.
Chromedriver.FindElement(By.Id("ctl00_MainContent_dpStart_DDD_C_mt")).Click();
new WebDriverWait(Chromedriver, TimeSpan.FromSeconds(5)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//td[#class='dxeCalendarDay'][.='1']"))).Click();

Seems you were pretty close. This error message...
OpenQA.Selenium.InvalidSelectorException: 'invalid selector: Unable to locate an element with the xpath expression //ttr/td[contains(text(), '>1<' because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//ttr/td[contains(text(), '>1<'' is not a valid XPath expression.
...implies that the XPath you have used wasn't a valid XPath Expression.
You have a couple of issues as follows:
The > and < are the part of the tags so shouldn't be a part of the XPath Expression. So you need to remove them.
You need to use either contains() or text() but not both.
The XPath Expression needs to be in proper format.
So your own XPath Expression with minor modifications will be as follows:
IWebElement FromCalendar1 = Chromedriver.FindElement(By.XPath("//tr/td[#class='dxeCalendarDay'][text()='1']"));
//or
IWebElement FromCalendar1 = Chromedriver.FindElement(By.XPath("//tr/td[#class='dxeCalendarDay' and contains(.,'1')]"));
Further, as you will be clicking on a button element for calendar popup to appear, you need to induce WebDriverWait for the element to be clickable as follows:
new WebDriverWait(Chromedriver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//tr/td[#class='dxeCalendarDay'][text()='1']"))).Click();
//or
new WebDriverWait(Chromedriver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//tr/td[#class='dxeCalendarDay' and contains(.,'1')]"))).Click();
Note: ChromeDriver is a reserved keyword which shouldn't be used as a user defined variable at any cost.

First I used this code
var query = driver.FindElement(By.Id("...")); // this will be looking for your (id/Css/Xpath..) and I created this 'var query to sent' the current DataTime
DateTime y = DateTime.Today; // this is my string (y) the current time
query.SendKeys(y.ToString()); then I send my string (y) to my query (My problem here was the click, so I created this code to waiting my string (y) and Click
new WebDriverWait(driver,TimeSpan.FromSeconds(5)).Until(ExpectedConditions.ElementToBeClickable(By.ClassName("rcToday"))).Click();
// Well when my string sent (y) my calendar would show me a DataTime but it weren't clickable, then I found the WebElement over the (y), in my case was (rcToday) and done I could click over it.
var query = driver.FindElement(By.Id("..."));
DateTime y = DateTime.Today;
query.SendKeys(y.ToString());
new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(ExpectedConditions.ElementToBeClickable(By.ClassName("rcToday"))).Click();
In other words, you can try it to pick up the day with this step bellow
driver.FindElement(By.Id**("popupButton")).Click();// look your element
river.FindElement(By.LinkText("13")).Click();// you could try by (Id/CssSelector/Xpath etc...) to click over the day, if the element is unclikable try to put the code bellow and look for the element the day is selected and click.
new WebDriverWait(driver, TimeSpan.FromSeconds(5)).Until(ExpectedConditions.ElementToBeClickable(By.ClassName("rcWeekend"))).Click();// this class name were my element that i found.

Related

How to use wildcards in CssSelector when finding an element through Selenium?

I have an issue where the CssSelector I am using has a GUID in it that changes every time and therefore the tests will only ever pass once. Is there a wildcard I can use in the CssSelector that will help me get round this? Consider the following code...
IWebElement PersonalPhone = Driver.driver.FindElement(By.CssSelector("# Grid365e0689-dccb-695f-97af-dc29187d4e1d-id-cell-0-7 > a"));
PersonalPhone.Click();
I would like the above code to locate the element via the CssSelector using a wildcard so that I can remove the GUID part of the selector and only find the element based on the last part 'id-cell-0-7' and then click on the element.
I am using Selenium WebDriver(Chrome) written in C#
Any ideas?
You can use partial id with contains *=
IWebElement PersonalPhone = Driver.driver.FindElement(By.CssSelector("[id*='id-cell-0-7'] > a"));
Or end with $=
IWebElement PersonalPhone = Driver.driver.FindElement(By.CssSelector("[id$='id-cell-0-7'] > a"));
The value of the id attribute looks dynamic to me so as an alternative you can use the following css-selectors:
IWebElement PersonalPhone = Driver.driver.FindElement(By.CssSelector("[id^='Grid'][id*='-id-cell-'] > a"));
PersonalPhone.Click();
Optimizing the lines of code in a single line:
Driver.driver.FindElement(By.CssSelector("[id^='Grid'][id*='-id-cell-'] > a")).Click();
Explanation
The id attribute:
Always start with Grid followed by the dynamic value, so you can use ^ to indicate starts-with
Always contain -id-cell- at the rear end, so you can use * to indicate contains
However, as the desired element is a dynamic element so to invoke click() on the element you may have to induce WebDriverWait for the ElementToBeClickable() and you can use either of the following Locator Strategies:
CssSelector:
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("[id^='Grid'][id*='-id-cell-'] > a"))).Click();
XPath:
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//*[starts-with(#id, 'Grid') and contains(#id, '-id-cell-')]/a"))).Click();
I am sorry to give you this news, but xpath 1.0 its still used in most drivers... quite:
As other answers have noted, XPath 1.0 does not support regular
expressions.
Suggested way is to use the parent elements to locate the element you wish to click.
or..
if the grid-xxx--xxx-- id keyword is constant you can do something like
Xpath: //*[starts-with(#id, 'Grid')]/a - id starts with Grid
CSS: input[id^='Grid'] > a - id starts with Grid
Change the input to the actual web element.

How to find a dynamic element and send text through Selenium and C#

EDIT: Not sure if it really helps to figure it out, but this is Sharepoint based.
I have an element with special character which the Webdriver can't locate.
var element = wait.Until(x => x.FindElement(By.Id("Tasrit_6aecdca9-e3b9-4141-ae36-d537784f9592_$TextField_inplacerte")));
element.SendKeys("foo");
I guess it is the $ that causes the problem.
On the contrary , I found it by using :
var element = wait.Until(x => x.FindElements(By.CssSelector("div[id*='Tasrit_6aecdca9-e3b9-4141-ae36-d537784f9592']")));
element[2].FindElement(By.TagName("p")).SendKeys("foo");
The test passes that way(seemingly), but the value isn't really being sent to the field.
Unfortunately, there is no input tag on the element's hierarchy, and when inserting the text manually , I can then see that the value was inserted to the <p> tag. But , as shown, when using the <p> tag , it doesn't really help.
The HTML:
<div class="ms-rtestate-write ms-rteflags-0 ms-rtestate-field" id="Tasrit_6aecdca9-e3b9-4141-ae36-d537784f9592_$TextField_inplacerte" role="textbox" aria-haspopup="true" aria-labelledby="Tasrit_6aecdca9-e3b9-4141-ae36-d537784f9592_$TextField_inplacerte_label" style="min-height: 84px;" contenteditable="true" aria-autocomplete="both" aria-multiline="true" RteDirty="true">
<p>
<span id="ms-rterangecursor-start" RteNodeId="1"></span>
<span id="ms-rterangecursor-end"></span>
​</p>
</div>
Instead of implementing two FindElement* you can do it in single step as follows:
CssSelector:
wait.Until(x => x.FindElement(By.CssSelector("div.ms-rtestate-write.ms-rteflags-0.ms-rtestate-field[id^='Tasrit_'][aria-labelledby$='_inplacerte_label']>p"))).SendKeys("foo");
XPath:
wait.Until(x => x.FindElement(By.XPath("//div[#class='ms-rtestate-write ms-rteflags-0 ms-rtestate-field' and starts-with(#id,'Tasrit_')][contains(#aria-labelledby,'_inplacerte_label')]/p"))).SendKeys("foo");
Update
However the element looks dynamic to me so you need to induce WebDriverwait for the desired element to be clickable and you can use either of the following solutions:
CssSelector:
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("div.ms-rtestate-write.ms-rteflags-0.ms-rtestate-field[id^='Tasrit_'][aria-labelledby$='_inplacerte_label']>p"))).SendKeys("foo");
XPath:
new WebDriverWait(driver, TimeSpan.FromSeconds(20)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//div[#class='ms-rtestate-write ms-rteflags-0 ms-rtestate-field' and starts-with(#id,'Tasrit_')][contains(#aria-labelledby,'_inplacerte_label')]/p"))).SendKeys("foo");
If SendKeys() doesn't work you can try using JavaScript
IWebElement webElement = element[2].FindElement(By.TagName("p"));
driver.ExecuteJavaScript("arguments[0].setAttribute('value', 'arguments[1]')", webElement, "foo");
And I guess there is extra space in that element's id :) Try this:
wait.Until(x => x.FindElement(By.Id("Tasrit_6aecdca9-e3b9-4141-ae36-d537784f9592_$TextField_inplacerte")));
element.Click();
Either that or the Id value of that element in the DOM is different, $TextField_inplacerte being a variable that parses to eg. spaceMonkey, undefined, 5 etc. Open up the dev tools, find the element, right-click, inspect and confirm the actual Id of the element in the DOM.
You can use dev tools API to search for text in DOM of the currently opened page (with that element in it) if it matches the Id matches if not there is a difference in it. It could be any part eg. 6th character in Id being different :)
Just to make sure that space between 1st part of id and 24 you mention is the problem you can either:
look at webdriver's code and see internally what is used to access DOM elements
load jquery before testing this out:
var element = wait.Until(x => x.FindElements($('#id containing
spaces')).SendKeys("foo");
Basically, instead of using webdriver way to find element, you use jQuery to obtain the element reference. If that works it's the space problem due to bad design of the application having space in element's id
Possibly that is why CSS selector route worked.

How to get the text which is outside of a element and changes in the run time using selenium c#?

I have piece of code as below
<div class="span6" style="text-align:right;">
<span class="muted" style="padding-left:20px;">Member ID: </span>MKL123451KKM
<span class="muted" style="padding-left:20px;">Service Date: </span>05/08/2018
</div>
in above code i want to get the value "MKL123451KKM", this value is going to change often.
i have tried with below xpaths which was giving error.
XPATH :
/html/body/div/span[1][contains(text(),'Member ID:')]/../text()
/html/body/div/span[1][contains(.,'Member ID:')]/../text()
ERROR :
The result of the xpath expression :
/html/body/div/span[1][contains(text(),'Member ID:')]/../text() is: [object Text]. It should be an element.
NOTE:
i am using selenium driver for IE and c# with VS 2015 IDE
Can anybody throw light on this?
You were fairly close. It's generally not a good practice to create a locator that starts at the HTML tag or has too many levels because it's more brittle (more likely to break when the page changes). Ideally you would find the label element by text label, e.g. "Member ID", and then locate the following text node. The big benefit of this method is that it's tied to the "Member ID" label. Some of the other answers are hard-coded to a specific text node which may be good now but if the data changes, it may return the wrong value.
You can wrap this all in a function where you pass in the label and it returns the value.
public string GetValue(string labelName)
{
IWebElement e = Driver.FindElement(By.XPath($"//span[contains(.,'{labelName}')]"));
string s = (string)((IJavaScriptExecutor)Driver).ExecuteScript("return arguments[0].nextSibling.textContent;", e);
return s.Trim();
}
and you would call it like
GetValue("Member ID")
As per the HTML you have shared the text MKL123451KKM is not within any child <span> node but within the parent <div> node. To extract the text e.g. MKL123451KKM you can use the following code block :
IWebElement elem = driver.FindElement(By.XPath("//div[#class='span6']"));
string text = (string)((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0].childNodes[2].textContent;", elem);
That's because the text is within the div. You will need to get text from xpath:
//div[#class='span6']
Even though this xpath isn't very change-proof, it should work if you get text from it. The text though will be MKL123451KKM and 05/08/2018
Try this XPath
//div[#class='span6']/span/following-sibling::text()[1]

Passing a value into an Xpath expression

I am currently writing a test against an e-commerce website. When a user runs a search for a particular product a list of items are being returned. What I am hoping to do is pass a particular value (e.g. the number 2) into my test scenario, at which point can be passed into my XPath expression (nth-child), enabling the item to be selected.
The XPath is incorrect and not sure how to fix it. Would appreciate if someone could help.
[Then(#"I select item '(.*)' from the search results")]
public static void WaitAndSelectAnItem(int item)
{
{
Driver.Instance.FindElements(By.CssSelector("#productGrid"));
var itemToSelect = Driver.Instance.FindElement(By.XPath(string.Format("//#class='itemContainer:nth-child({0})'", item)));
itemToSelect.Click();
}
}
Result Message: The given selector //#class='itemContainer:nth-child(3)' is either invalid or does not result in a WebElement. The following error occurred:
InvalidSelectorError: Unable to locate an element with the xpath expression //#class='itemContainer:nth-child(3)' because of the following error:
TypeError: The expression cannot be converted to return the specified type.
nth-child is not part of the XPath language and is actually a CSS selector pseudo-class.
Instead, you've probably meant:
(//*[#class='itemContainer'])[{0}]
Note that indexing in XPath starts with 1 - make sure you are actually getting the correct item.
Alternatively, you can use findElements() to find all "itemContainers" and then get the desired one by index. Note that it is 0-indexed here:
Driver.Instance.FindElements(By.XPath("//*[#class='itemContainer']"))[item]
Assuming that the "item container" is a DIV tag, if not div replace div by the right tag, * will work as well but there may be chances that you get more elements which you don't want.
Try this:
(//div[#class='itemContainer'])[1]
Replace 1 by your item indices.

How to Identify this element using Selenium Webdriver

I have tried finding the element by Id, XPath, and CssSelector but have not had any luck. The test times out while looking for the element.
<div xmlns="http://www.w3.org/1999/xhtml" id="HyperlinkBetaSentry"
style="top:0px;left:521px;height:24px;width:70px;cursor:pointer;text-align:center"
class="DashboardHyperlink"><a target="_blank"
href="https://salsa.sentry.com/SalsaDataSentry/wafForm.aspx?__sso=1234567890" style="color:#e6e7e8;vertical-align:middle;text-decoration:none">Beta Sentry</a></div>
I have tried:
var title = Driver.Instance.FindElement(By.CssSelector("#HyperlinkBetaSentry"));
var title = Driver.Instance.FindElement(By.Xpath("//*[#id='HyperlinkBetaSentry']"));
var title = Driver.Instance.FindElement(By.Id("HyperlinkBetaSentry"));
I would think By.Id should be working. Usually when this happens it's because the code looking for the element is executed before the element exists (e.g. the DOM isn't fully loaded, or some Javascript changed the DOM, or an Ajax request, etc.)
As a test to confirm. Set a break point in your code just before looking for the element. Make sure the element does in fact exist as you expect, then step through the code that tries to find it. Chances are it'll find it.
The only other thing that sometimes causes this is that you're looking into a different frame than where your element is.
There are multiple issues that can cause this.
Pass the click to a tag
var title = Driver.Instance.FindElement(By.CssSelector("#HyperlinkBetaSentry>a"));
Element Load time
Duplicate ids
In case 2: You can define some explicit wait for the element to be visible.
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.presenceOfElementLocated(By.id("YourID")));
See here for explanation.
In case 3: You might want to use a text base search using xpath or css with multiple attributes.
.//*[.='Beta Sentry']

Categories

Resources