XPath / Selenium can't locate an element using a partial id with contains / start-with - xpath

I have the following HTML generated with an AjaxFormLoop.
<div id="phones">
<div class="t-forminjector tapestry-forminjector" id="rowInjector_13b87fdd8b6">
<input id="number_13b87fdd8b6" name="number_13b87fdd8b7" type="text"/>
<a id="removerowlink_13b87fdd8b6" href="#" name="removerowlink_13b87fdd8b6">remove</a>
</div>
<div class="t-forminjector tapestry-forminjector" id="rowInjector_13b87fdda70" style="background-image: none; background-color: rgb(255, 255, 251);">
<input id="number_13b87fdda70" name="number_13b87fdda70" type="text" />
<a id="removerowlink_13b87fdda70" href="#" name="removerowlink_13b87fdda70">remove</a>
</div>
</div>
I'm trying to access the second input field in child 2 using a partial ID, however I have not been successful in getting this to work.
What I've tried thus far.
String path = "//input[contains(#id,'number_')][2]";
String path = "(//input[contains(#id,'number_')])[2]";
I can't even access input 1 using 1 instead of 2, however if I remove [2] and only use
String path = "//input[contains(#id,'number_')]";
I'm able to access the first field without issue.
If I use the exact id, I'm able to access either field without issue.
I do need to use the id if possible as there is many more fields in each t-forminjector row that are not present in this example.
Implementation with Selenium.
final String path = "(//input[starts-with(#id,'quantity_')])[2]";
new Wait() {
#Override
public boolean until() {
return isElementPresent(path);
}
}.wait("Element should be present", TIMEOUT);
Resolved
I'm noticing I can't seem to use the following starts-with / contains to locate any element within to dom, however if I use a complete id, it works.
//Partial ID - fails
//*[starts-with(#id,"quantity_")]
//Exact ID - works
//*[starts-with(#id,"quantity_-112409575185705")]

The generated output you pasted here simply does not contain the string number_ anywhere in it. It does contain Number_ -- note the capital N -- but it's not the first part of the string. Perhaps you meant something like this (which at least selects something):
(//input[contains(#id, 'Number_')])[2]
Or:
(//input[starts-with(#id,'catalogNumber_')])[2]

As Iwburk stated, this was a namespace issue. According to the Selenium API,
http://release.seleniumhq.org/selenium-remote-control/0.9.0/doc/java/com/thoughtworks/selenium/Selenium.html
while using an xpath expression, I needed to used xpath=xpathExpression changing my query string to:
String path = "xpath=(//input[starts-with(#id,'quantity_')])[2]";
I found a related post here,
Element is found in XPath Checker but not in Selenium

you can't access it because you are not locating the element as to be unique in the page.
use an xpath that makes it unique ,
- you're xpath look ok .
more info here
http://www.seleniumhq.org/docs/appendix_locating_techniques.jsp

Besides the selenium syntax problem there's an xpath issue related to markup structure.
xpath 1: //input[starts-with(#id,'number_')][1]
xpath 2: (//input[starts-with(#id,'number_')])[1]
In the sample below xpath 1 will return 2 nodes (incorrect) and xpath 2 will be correct because input nodes are not siblings so surrounding parenthesis are needed to refer to the resulting nodeset
<div id="phones">
<div>
<input id="number_1" name="number_1" type="text"/>
</div>
<div>
<input id="number_2" name="number_2" type="text" />
</div>
</div>
Result without parenthesis
/ > xpath //input[starts-with(#id,'number_')][1]
Object is a Node Set :
Set contains 2 nodes:
1 ELEMENT input
ATTRIBUTE id
TEXT
content=number_1
ATTRIBUTE name
TEXT
content=number_1
ATTRIBUTE type
TEXT
content=text
2 ELEMENT input
ATTRIBUTE id
TEXT
content=number_2
ATTRIBUTE name
TEXT
content=number_2
ATTRIBUTE type
TEXT
content=text
In this next sample, parenthesis will not make a difference because nodes are siblings
<div id="other">
<input id="pre_1" type="text"/>
<input id="pre_2" type="text" />
<div>a</div>
</div>
With parenthesis
/ > xpath (//input[starts-with(#id,'pre_')])[1]
Object is a Node Set :
Set contains 1 nodes:
1 ELEMENT input
ATTRIBUTE id
TEXT
content=pre_1
ATTRIBUTE type
TEXT
content=text
Without parenthesis
/ > xpath //input[starts-with(#id,'pre_')][1]
Object is a Node Set :
Set contains 1 nodes:
1 ELEMENT input
ATTRIBUTE id
TEXT
content=pre_1
ATTRIBUTE type
TEXT
content=text
Testing was done with xmllint shell
xmllint --html --shell test.html

Related

Find text in xpath

How to access "Testing Field 1" from Label without using contains or adding span tag in Xpath
<label id="138:" for="136:" class="rcmFormFieldLabel">
<span class="requiredField" aria-hidden="true" role="presentation">*</span>
Testing Field 1
</label>
my xpath
//label[contains(text(),'Testing Field 1')]/ancestor::div[1]//select
but my xpath have contains which i dont want
You can use these XPath-1.0 expressions:
This
//label/*/following-sibling::text()[normalize-space()]
Or this
//label/text()[normalize-space()]
Result in both cases is
Testing Field 1

How to exclude a child node from xpath?

I have the following code :
<div class = "content">
<table id="detailsTable">...</table>
<div class = "desc">
<p>Some text</p>
</div>
<p>Another text<p>
</div>
I want to select all the text within the 'content' class, which I would get using this xPath :
doc.xpath('string(//div[#class="content"])')
The problem is that it selects all the text including text within the 'table' tag. I need to exclude the 'table' from the xPath. How would I achieve that?
XPath 1.0 solutions :
substring-after(string(//div[#class="content"]),string(//div[#class="content"]/table))
Or just use concat :
concat(//table/following::p[1]," ",//table/following::p[2])
The XPath expression //div[#class="content"] selects the div element - nothing more and nothing less - and applying the string() function gives you the string value of the element, which is the concatenation of all its descendant text nodes.
Getting all the text except for that containing in one particular child is probably not possible in XPath 1.0. With XPath 2.0 it can be done as
string-join(//div[#class="content"]/(node() except table)//text(), '')
But for this kind of manipulation, you're really in the realm of transformation rather than pure selection, so you're stretching the limits of what XPath is designed for.

How to get descendants with a specific tag name and text in protractor?

I have the following structure (it's just for sample). In protractor, I am getting the top element by id. However, the other elements do not have id's. I need to get the "label" element that contains the text '20'. Is there an easy way in protractor to select the element with a specific tag that contains a specific text from all the descendants of a parent element?
<pc-selector _... id="Number1">
<div ...></div>
<div ...>
<div ...>
<check-box _...>
<div _ngcontent-c25="" ...>
<label _ngcontent-c25="">
<input _ngcontent-c25="" type="checkbox">
<span _ngcontent-c25="" class="m-checkbox__marker"></span>
20 More text to follow</label>
</div>
</check-box>
</div>
</div>
</pc-selector>
I could't find anythitng, so I have tried with xpath, but protractor complains that my xpath is invalid:
parentElement = element(by.id('Number1'));
return parentElement.element(by.xpath(".//label[contains(text(),'20'))]"));
Any ideas?
You have an additional bracket in your [contains(text(),'20'))] which is likely causing you issue but there are multiple other ways this can be achieved using a single XPath or chaining other locators.
The process is that you must find the div with the correct id first and then locate the label that is a child of it.
//Xpath
element(by.xpath("//pc-selector[#id='Number1']//label[contains(text(),'20')]"));
//Chained CSS
element(by.id('Number1')).element(by.cssContainingText('label','20'));
You also may be interested to learn about xpath axes which can allow us to do very dynamic selection.
You can use the direct xpath to access the label.
element(by.xpath("//*[#id='Number1']//label"));

Access two elements simultaneously in Nokogiri

I have some weirdly formatted HTML files which I have to parse.
This is my Ruby code:
File.open('2.html', 'r:utf-8') do |f|
#parsed = Nokogiri::HTML(f, nil, 'windows-1251')
puts #parsed.xpath('//span[#id="f5"]//div[#id="f5"]').inner_text
end
I want to parse a file containing:
<span style="position:absolute;top:156pt;left:24pt" id=f6>36.4.1.1. варенье, джемы, конфитюры, сиропы</span>
<div style="position:absolute;top:167.6pt;left:24.7pt;width:709.0;height:31.5;padding-top:23.8;font:0pt Arial;border-width:1.4; border-style:solid;border-color:#000000;"><table></table></div>
<span style="position:absolute;top:171pt;left:28pt" id=f5>003874</span>
<div style="position:absolute;top:171pt;left:99pt" id=f5>ВАРЕНЬЕ "ЭКОПРОДУКТ" ЧЕРНАЯ СМОРОДИНА</div>
<div style="position:absolute;top:180pt;left:99pt" id=f5>325гр. </div>
<div style="position:absolute;top:167.6pt;left:95.8pt;width:2.8;height:31.5;padding-top:23.8;font:0pt Arial;border-width:0 0 0 1.4; border-style:solid;border-color:#000000;"><table></table></div>
I need to select either <div> or <span> with id==5. With my current XPath selector it's not possible. If I remove //span[#id="f5"], for example, then the divs are selected correctly. I can output them one after another:
puts #parsed.xpath('//div[#id="f5"]').inner_text
puts #parsed.xpath('//span[#id="f5"]').inner_text
but then the order would be a complete mess. The parsed span have to be directly underneath the div from the original file.
Am I missing some basics? I haven't found anything on the web regarding parallel parsing of two elements. Most posts are concerned with parsing two classes of a div for example, but not two different elements at a time.
If I understand this correctly, you can use the following XPath :
//*[self::div or self::span][#id="f5"]
xpathtester demo
The XPath above will find element named either div or span that have id attribute value equals "f5"
output :
<span id="f5" style="position:absolute;top:171pt;left:28pt">003874</span>
<div id="f5" style="position:absolute;top:171pt;left:99pt">ВАРЕНЬЕ "ЭКОПРОДУКТ" ЧЕРНАЯ СМОРОДИНА</div>
<div id="f5" style="position:absolute;top:180pt;left:99pt">325гр.</div>

How to write the single xpath when the text is in two lines

How to write the single xpath for this
<div class="col-lg-4 col-md-4 col-sm-4 profilesky"> <div class="career_icon">
<span> Boost </span> <br/>
Your Profile </div>
I am able to write by two line using "contains" method.
.//*[contains(text(),'Boost')]
.//*[contains(text(),'Your Profile')]
But i want in a single line to write the xpath for this.
You can try this way :
.//*[#class='career_icon' and contains(., 'Boost') and contains(., 'Your Profile')]
Above xpath check if there is an element having class attribute equals career_icon and contains both Boost and Your Profile texts in the element body.
Note that text() only checks direct child text node. To check entire text content of an element simply use dot (.).
You can combine several rules just by writing them one after another since they refer to the same element:
.//[contains(text(),'Boost')][contains(text(),'Your Profile')]

Resources