I have started using Page factory, and now i need to provide xpath in #FindBy. It will be great if someone can provide any suggestion or reference on how to pass variable in xpath using #Findby.
Element which i want to replace with #Findby annotation
for(i=1; i <= liElements.size(); i++) {
WebElement linkElement = driver.findElement(By.xpath("//li[" + i + "]/div//a[contains(#class, 'btn-mini')]"));
linkElement.click();
}
Thanks a lot in advance for your help.
Regarding your question around parametrised FindBy - this is not possible as annotations are constant values. Take a look here - Can the annotation variables be determined at runtime?
In this particular case you can find a list of the elements in FindBy:
#FindBy(xpath = "//li/div//a[contains(#class, 'btn-mini')]")
private List<WebElement> links;
Then you can iterate through them like this:
for(WebElement link : links) {
link.click();
}
So you will click links found by that xPath one by one.
Related
I have a while loop containing an Xpath expression. How can I have the while loop working properly with only the value of tr[index] changing within the Xpath? Below is an example:
//this is what I currently have:
while(webDriver.findElement(By.xpath("//*[#id='sharing_list']/tbody/tr[1]/td[4]")).isDisplayed())
{
webDriver.findElement(By.xpath("//*[#id='sharing_list']/tbody/tr[1]/td[4]/span")).click();
}
//and this is what I would like to have:
int n=1;
while(webDriver.findElement(By.xpath("//*[#id='sharing_list']/tbody/tr[n]/td[4]")).isDisplayed())
{
webDriver.findElement(By.xpath("//*[#id='sharing_list']/tbody/tr[n]/td[4]/span")).click();
n++;
}
Does any one have an idea how to make it work? I am using Selenium 2.33.
Your help will be very appreciated.
This is really a String issue. Replace your tr[n] with tr["+n+"]
I've been through the xpath tutorials and checked many other posts, hence I'm not sure what I'm missing. I'm simply trying to find the following element by xpath:
<input class="t-TextBox" type="email" test-id="test-username"/>
I've tried many things, such as:
element = findElement(By.xpath("//[#test-id='test-username']"));
The error is Expression is not a legal expression.
I'm using Firefox on MacBook
Any suggestion would be greatly appreciated.
element = findElement(By.xpath("//*[#test-id='test-username']"));
element = findElement(By.xpath("//input[#test-id='test-username']"));
(*) - means any tag name.
You should add the tag name in the xpath, like:
element = findElement(By.xpath("//input[#test-id='test-username']");
your syntax is completely wrong....you need to give findelement to the driver
i.e your code will be :
WebDriver driver = new FirefoxDriver();
WebeElement element ;
element = driver.findElement(By.xpath("//[#test-id='test-username']");
// your xpath is: "//[#test-id='test-username']"
i suggest try this :"//*[#test-id='test-username']"
You missed the closing parenthesis at the end:
element = findElement(By.xpath("//[#test-id='test-username']"));
Just need to add * at the beginning of xpath and closing bracket at last.
element = findElement(By.xpath("//*[#test-id='test-username']"));
You can use contains too:
element = findElement(By.xpath("//input[contains (#test-id,"test-username")]");
You haven't specified what kind of html element you are trying to do an absolute xpath search on. In your case, it's the input element.
Try this:
element = findElement(By.xpath("//input[#class='t-TextBox' and #type='email' and #test-
id='test-username']");
Correct Xpath syntax is like:
//tagname[#value='name']
So you should write something like this:
findElement(By.xpath("//input[#test-id='test-username']"));
I am trying to click an element that changes per each order like so
edit_div_123
edit_div_124
edit_div_xxx
xxx = any three numbers
I have tried using regex like so:
#driver.find_element(:css, "#edit_order_#{\d*} > div.submit > button[name=\"commit\"]").click
#driver.find_element(:xpath, "//*[(#id = "edit_order_#{\d*}")]//button").click
Is this possible? Any other ways of doing this?
You cannot use Regexp, like the other answers have indicated.
Instead, you can use a nifty CSS Selector trick:
#driver.find_element(:css, "[id^=\"edit_order_\"] > div.submit > button[name=\"commit\"]").click
Using:
^= indicates to find the element with the value beginning with your criteria.
*= says the criteria should be found anywhere within the element's value
$= indicates to find the element with with your criteria at the end of the value.
~= allows you to find the element based on a single criteria when the actual value has multiple space-seperated list of values.
Take a look at http://net.tutsplus.com/tutorials/html-css-techniques/the-30-css-selectors-you-must-memorize/ for some more info on other neat CSS tricks you should add to your utility belt!
You have no provided any html fragment that you are working on. Hence my answer is just based on the limited inputs provided your question.
I don't think WebDriver APIs support regex for locating elements. However, you can achieve what you want using just plain XPath as follows:
//*[starts-with(#id, 'edit_div_')]//button
Explanation: Above xpath will try to search all <button> nodes present under all elements whose id attribute starts with string edit_div_
In short, you can use starts-with() xpath function in order to match element with id format as edit_div_ followed by any number of characters
No, you can not.
But you should do something like this:
function hasClass(element, className) {
var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
return re.test(element.className);
}
This worked for me
#driver.find_element(:xpath, "//a[contains(#href, 'person')]").click
I'm pretty confused about this one. Given the following xml:
<sch:eventList>
<sch:event>
<sch:eventName>Event One</sch:eventName>
<sch:locationName>Location One</sch:locationName>
</sch:event>
<sch:event>
<sch:eventName>Event Two</sch:eventName>
<sch:locationName>Location Two</sch:locationName>
</sch:event>
</sch:eventList>
When using JDOM using the following code:
XPath eventNameExpression = XPath.newInstance("//sch:eventName");
XPath eventLocationExpression = XPath.newInstance("//sch:eventLocation");
XPath eventExpression = XPath.newInstance("//sch:event");
List<Element> elements = eventExpression.selectNodes(requestElement);
for(Element e: elements) {
System.out.println(eventNameExpression.valueOf(e));
System.out.println(eventLocationExpression.valueOf(e));
}
The console shows this:
Event One
Location One
Event One
Location One
What am I missing?
Don't use '//' it starts always searching at the root node. Use e.g. './sch:eventName' it is relative to the current node.
Using the HTMLAgilityPack to write out a new image node, it seems to remove the closing tag of an image, e.g. should be but when you check outer html, has .
string strIMG = "<img src='" + imgPath + "' height='" + pubImg.Height + "px' width='" + pubImg.Width + "px' />";
HtmlNode newNode = HtmlNode.Create(strIMG);
This breaks xhtml.
Telling it to output XML as Micky suggests works, but if you have other reasons not to want XML, try this:
doc.OptionWriteEmptyNodes = true;
Edit 1:Here is how to fix an HTML Agilty Pack document to correctly display image (img) tags:
if (HtmlNode.ElementsFlags.ContainsKey("img"))
{ HtmlNode.ElementsFlags["img"] = HtmlElementFlag.Closed;}
else
{ HtmlNode.ElementsFlags.Add("img", HtmlElementFlag.Closed);}
replace "img" for any other tag to fix them as well (input, select, and option come up frequently). Repeat as needed. Keep in mind that this will produce rather than , because of the HAP bug preventing the "closed" and "empty" flags from being set simultaneously.
Source: Mike Bridge
Original answer:
Having just labored over solutions to this issue, and not finding any sufficient answers (doctype set properly, using Output as XML, Check Syntax, AutoCloseOnEnd, and Write Empty Node options), I was able to solve this with a dirty hack.
This will certainly not solve the issue outright for everyone, but for anyone returning their generated html/xml as a string (EG via a web service), the simple solution is to use fake tags that the agility pack doesn't know to break.
Once you have finished doing everything you need to do on your document, call the following method once for each tag giving you a headache (notable examples being option, input, and img). Immediately after, render your final string and do a simple replace for each tag prefixed with some string (in this case "Fix_", and return your string.
This is only marginally better in my opinion than the regex solution proposed in another question I cannot locate at the moment (something along the lines of )
private void fixHAPUnclosedTags(ref HtmlDocument doc, string tagName, bool hasInnerText = false)
{
HtmlNode tagReplacement = null;
foreach(var tag in doc.DocumentNode.SelectNodes("//"+tagName))
{
tagReplacement = HtmlTextNode.CreateNode("<fix_"+tagName+"></fix_"+tagName+">");
foreach(var attr in tag.Attributes)
{
tagReplacement.SetAttributeValue(attr.Name, attr.Value);
}
if(hasInnerText)//for option tags and other non-empty nodes, the next (text) node will be its inner HTML
{
tagReplacement.InnerHtml = tag.InnerHtml + tag.NextSibling.InnerHtml;
tag.NextSibling.Remove();
}
tag.ParentNode.ReplaceChild(tagReplacement, tag);
}
}
As a note, if I were a betting man I would guess that MikeBridge's answer above inadvertently identifies the source of this bug in the pack - something is causing the closed and empty flags to be mutually exclusive
Additionally, after a bit more digging, I don't appear to be the only one who has taken this approach:
HtmlAgilityPack Drops Option End Tags
Furthermore, in cases where you ONLY need non-empty elements, there is a very simple fix listed in that same question, as well as the HAP codeplex discussion here: This essentially sets the empty flag option listed in Mike Bridge's answer above permanently everywhere.
There is an option to turn on XML output that makes this issue go away.
var htmlDoc = new HtmlDocument();
htmlDoc.OptionOutputAsXml = true;
htmlDoc.LoadHtml(rawHtml);
This seems to be a bug with HtmlAgilityPack. There are many ways to reproduce this, for example:
Debug.WriteLine(HtmlNode.CreateNode("<img id=\"bla\"></img>").OuterHtml);
Outputs malformed HTML. Using the suggested fixes in the other answers does nothing.
HtmlDocument doc = new HtmlDocument();
doc.OptionOutputAsXml = true;
HtmlNode node = doc.CreateElement("x");
node.InnerHtml = "<img id=\"bla\"></img>";
doc.DocumentNode.AppendChild(node);
Debug.WriteLine(doc.DocumentNode.OuterHtml);
Produces malformed XML / XHTML like <x><img id="bla"></x>
I have created a issue in CodePlex for this.