The element that I’m talking about is the following:
<tbody>
<tr
id="tableUsers_0__TR"
class="active"
name="Tr_tableUsers[0]_Selected">
<td>
<input
id="tableUsers_0__POSName"
type="hidden"
value="Indian"
name="tableUsers[0].POSName"/>
Indian
</td>
The solution that I propose is:
The first step is locate the element by XPath:
string xpath = ".//*[#id='tableUsers_0__PDVCode']/..";
Then get the text with the method:
driver.FindElement(By.XPath(xpath)).Text
Is it the best way? Or not?
Is there another better way than this way? Better than the use of XPath?
JavaScript Executor can also be used to return the element's text. The following script can be used:
document.getElementById('<your id>').innerText
or
document.getElementById('<your id>').textContent
To get this, the object of JavascriptExecutor has to be created first. Refer to the following script:
IWebDriver driver; // A driver object is created in the code somewhere
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
string valueFromId = (string)js.ExecuteScript("return document.getElementById('<your id>').innerText");
You can also verify this text by running the JavaScript command (document.ge...) in the browser's console.
You can use the selector By.Id("your id").
For some elements, it’s much more useful to use IWebElement.GetAttribute("text") than IWebElement.Text
Yes, a better way than using an XPath expression is to use By.id as:
driver.FindElement(By.Id("tableUsers_0__TR")).Text
Or use By.CssSelector:
driver.FindElement(By.CssSelector("tr#tableUsers_0__TR td")).Text
Note: It always gives the last priority to the XPath locator, because it is much slower than other locators. In your case, if the element has an id attribute then the best way is to use By.Id(). Otherwise, try to locate the element using other locators, like By.Name(), By.ClassName(), By.CssSelector(), etc. if it could be possible.
Related
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.
I am trying to do some automated tests with Selenium, but I have a problem. I want to be able to press a special button, but this button does not have an ID or Class, which is the first thing I try to look for.
I Think that I need to use the CssSelector, but how exactly should I write to find the correct button in my test?
driver.FindElement(By.CssSelector(??))
You can use the sibling <table> id as starting point
driver.FindElement(By.CssSelector("#tbl_bestallningslita ~ button"));
Or use the onclick attribute (partial)
driver.FindElement(By.CssSelector("[onclick*='btn_taBort']"));
Any of the below CSSLocators should help:
button[accessKey="T"]
OR
button[onclick="folkbokforing_form.btn_taBort.click()"]
You can use xpath too, xpath gives you more flexibility over cssselector although its quite slower than cssselector.
driver.findElement(By.XPath("*//button[#onclick='folkbokforging_form.btn.taBort.click()']"));
or use combined two attributed xpath,
driver.findElement(By.XPath("*//button[#accesskey='T' and contains(#onlcinck,'btn_taBort.click()')]");
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]
I have XPath
/html/body/div[#id='page']/div[#id='page-inner']/div[#id='main-box']/div[#class='in1']/div[#id='content-and-context']/div[#id='content']/div[#class='under-bar']/table[#class='flights']/tbody/tr[#id='flight-932539']/td[2]:
But flight-number are changes. Can I find Elements with part XPath ?
I use foreach() and write data for every flight.
this is html code:
First thing first: don't use absolute path. Even the smallest change in the html invalidate the path, especially in dynamic applications. Your xpath could easily be //tr[#id='flight-932539']/td[2]
As for your question, you can use contains() for partial id
//tr[contains(#id, 'flight-')]/td[2]
As Guy mentioned xpath above, for same you can easily use findElements to find all the flight details and then according perform you actions using for loop.
List<WebElement> WebElements = driver.findElements(By.xpath("//tr[contains(#id, 'flight-')]/td[2]");
for(WebElement element : WebElements){
//perform any operation like for click you can use
element.getText();
}
Above example is in JAVA you can do same in C# as well.
I would like a to find a way to store the text from within a read only element.
IWebElement LName = Driver.Instance.FindElement(By.XPath(".//*[#id='inputGrid']/div[1]/div[1]/div/div[1]/table/tbody/tr[1]/td[4]"));
string LNValue = LName.GetAttribute("value");
The result of this code is that LNValue is empty.
However, LName is a cell that contains this text: "********"
If I inspect the element, I can see this:
<td class="htDimmed fill">********</td>
<td class="htDimmed">*********</td>
Since code is being written in C#, you can use built in "Text" property to fetch the innerHTML of that element and it should work in all browsers.
String LNValue= LName.Text;
Moreover make sure that XPath is correctly reaching the element as well..I hope this helps
You can use the textContent attribute to get the text. AFAIK, the td element doesnot have a value attribute.
string LNValue = LName.GetAttribute("textContent");
You could also use the innerText attribute, but that is not supported across all browsers.