Select one of multiple elements within a cell in xPath - php

I am using Behat and am trying to select some form elements.
My HTML is as follows - Basically I have two required fields, one an input and one a textarea, and a checkbox.
<table class="table">
<thead>
<tr>
<th>Delete</th>
<th>Question</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" name="delete" /></td>
<td><textarea name="question" required="required"></textarea></td>
<td><input type="text" required="required" name="reference" /></td>
</tr>
</tbody>
</table>
My xpath selector that gets the form elements is:
//table/tbody/tr/td/input
And this works fine for inputs obviously - but when I added the textarea and tried to check it, it can't find the textarea, because duh, it's not an input.
I tried this: //table/tbody/tr/td/(input or textarea) and several variations of using "or", |, etc. I can't figure out how to specify multiple types of elements to try to match.
Thanks In Advance! :)

PHP's XPath Processor only supports XPath 1.0, which does not allow alternations in path steps. A valid XPath 2.0 expression would have been
//table/tbody/tr/td/(input, textarea)
XPath 1.0 requires you to either provide full paths like this:
//table/tbody/tr/td/input | //table/tbody/tr/td/textarea
or use a predicate with name-test while using the wildcard node test:
//table/tbody/tr/td/*[local-name() = 'input' or local-name() = 'textarea']
The latter version will be probably preferable regarding performance as the XML file will only be scanned once.

Untested in Behat/PHP, but this is how it would look if following the XPath syntax.
//table/tbody/tr/td/input | //table/tbody/tr/td/textarea

Related

Returning specific DIV via specific TAG - PHP XPATH [duplicate]

I'm using Html Agility Pack to run xpath queries on a web page. I want to find the rows in a table which contain a certain interesting element. In the example below, I want to fetch the second row.
<table name="important">
<tr>
<td>Stuff I'm NOT interested in</td>
</tr>
<tr>
<td>Stuff I'm interested in</td>
<td><interestingtag/></td>
<td>More stuff I'm interested in</td>
</tr>
<tr>
<td>Stuff I'm NOT interested in</td>
</tr>
<tr>
<td>Stuff I'm NOT interested in</td>
</tr>
</table>
I'm looking to do something like this:
//table[#name='important']/tr[has a descendant named interestingtag]
Except with valid xpath syntax. ;-)
I suppose I could just find the interesting element itself and then work my way up the parent chain from the node that's returned, but it seemed like there ought to be a way to do this in one step and I'm just being dense.
"has a descendant named interestintag" is spelled .//interestintag in XPath, so the expression you are looking for is:
//table[#name='important']/tr[.//interestingtag]
Actually, you need to look for a descendant, not a child:
//table[#name='important']/tr[descendant::interestingtag]
I know this isn't what the OP was asking, but if you wanted to find an element that had a descendant with a particular attribute, you could do something like this:
//table[#name='important']/tr[.//*[#attr='value']]
I know it is a late answer but why not going the other way around. Finding all <interestingtag/> tags and then select the parent <tr> tag.
//interestingtag/ancestor::tr

Testing for a string in a particular element node

in PHP Unit (using Selenium Server) i'm trying to check if a particular element node in xpath has a certain string value, for instance
<table>
<thead></thead>
<tbody>
<tr>
<th>1</th>
<td>value 1</td>
<td>value 2</td>
<td>value c</td>
</tr>
<tr>
<th>2</th>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
with the above code, using the xpath
isElementPresent('//table/tbody/tr[last()-0]/td[last()-1]');
it would return true, if i changed tr[last()-0] to tr[last()-1] it would still return true,
naturally, the isElementPresent would be in a loop with the xpath generated in the loop as well (substituting the integer for $i and $j which are used in the for() loop) as as it is, that would be fine, however, what i want to check is that the has nothing in it
using the same html code above, if i change the xpath
isElementPresent('//table/tbody/tr[last()-$i]/td[last()-1 and text()="${nbsp}"]');
you would think that it would return true at //table/tbody/tr[last()-0]/td[last()-1 and text()="${nbsp}"] and false at //table/tbody/tr[last()-1]/td[last()-1 and text()="${nbsp}"] however here is the kicker
using Selenium IDE 1.10.0 Plugin for Firefox to check the xpath by putting it in the Target Box and hitting find (to check that it will locate the xpath when it should, //table/tbody/tr[last()-0]/td[last()-1 and text()="${nbsp}"] doesn't highlight the 2nd last td in the last tr, it highlights the first td in the last tr, as if the xpath was //table/tbody/tr[last()-0]/td[last()-2]
from my experiments, it seems to be treating the xapth like //table/tbody/tr[last()-0]/td[text()="${nbsp}"] which would only be the FIRST instance in which text is a blank space, not good if the 2nd tr was like
<tr>
<th>2</th>
<td>cows are my friends</td>
<td>let's go to my room pig!</td>
<td> </td>
</tr>
and i was to use isElementPresent('//table/tbody/tr[last()-0]/td[last()-1 and text()="${nbsp}"]'); it would still return true as its not looking at last()-1 but last()-0
so my question is, how can i check if a particular element node has a certain string
NOTE 1: i use last()-# cause on this page http://www.w3schools.com/xpath/xpath_syntax.asp it says IE5 and later says [0] is the first not and not [1] like Firefox or Chrome which in a sense makes sense since that's how an array's index works, for full compatibility, i start from the last Node and work backwards which last() would work with IE5 and later and the logic of moving backwards though the nodes should be the same (unless microsoft wants to redefine that logic)
NOTE 2: i am well aware that a simple fix is to add title or id attributes to the table however the page i'm making the test for was done by someone else, i would like to avoid modifying the page just to suit test cases
NOTE 3: the table i'm testing is populated using a JSON string so my test is when there is no data for the table, is it blank, if not, than the JSON string is adding data to the table when it shouldn't
EDIT 1: seems like ${nbsp} doesn't work in php, only in the Selenium IDE 1.10.0 Plugin seemed to recognize it however inserting a space by holding Alt and typing in 0160 worked just as fine
EDIT 2: for the time being i have added id attributes to the tags to get this working and it works perfectly fine with checking #id=[VALUE] and text()=[VALUE] but it would still be good to get this question answered as while i add id, title and/or class attributes to all my html tags the person who originally made the table i was testing obviously didn't and as i said in NOTE 2, 'i would like to avoid modifying the page just to suit test cases'
#Memor-X Use SimpleXML to load the xml. Then it should be easy to test in phpunit. If I was you I would build in dependency tests to validate the xml structure before testing the contents.

PHP DOMXPath content match & select expression

I'm trying to match and select a bunch of cols of a table but don't get it working. Here's a simplified table:
<table>
<tr>
<td>Foo</td>
<td>Bar</td>
<td>Arb</td>
<td>...</td>
</tr>
<tr>
<td>Foo</td>
<td>Rab</td>
</tr>
</table>
So I want to get the TDs wich contain Bar and Arb and others but not Foo and nothing from the 2nd TR Block. Someone knows if this is possible with a XPath expression?
Note: There's nothing static in there. The only way to get the correct cols is to match the first TDs content.
Don't know if my answer could help you out, but an adaptable XPath could look like:
//table/tr[1]/td[x] | //table/tr[1]/td[y]
where x and y depends which column you'd like to target. The | computes two node-sets.
I'd also like to suggest you to install the XPath Checker Firefox AddOn ( https://addons.mozilla.org/en-US/firefox/addon/xpath-checker/ ) where you're able to play around with the xPath syntax, in order to trigger a particular DOM Element of a website.

Sending special characters in HTML form

I have an input field (which is filled automatically) with the format name <myemail#host.com>. I gave the form enctype="application/x-www-form-urlencoded", but when I retrieve it in PHP, it shows only the name. Please help me retrieving the email too.
My HTML form:
<form action="{$path_site}{$index_file}" method="POST" enctype="application/x-www-form-urlencoded">
<table>
<tr>
<td>Your Name</td>
<td><input type="text" name="sender_name" size="37" /></td>
</tr>
<tr>
<td>To</td>
<td><input type="text" name="reciever_name" size="37" id="inputString" onkeyup="lookup(this.value)" onblur="fill()" /></td>
</tr>
</table>
</form>
And PHP code:
echo $msg_sender_name = $info[reciever_name];
Extracting the information from comments, where you say:
if the text is like "myName<myEmail#email.com>", info['reciever_name'] displays only "myName"
I would say that your problem is related to the displaying the results, and is not related to the form.
You probably display the received string as HTML, where the characters "<" and ">" are special.
Instead of
echo $info['reciever_name'];
you should use the htmlspecialchars function:
echo htmlspecialchars($info['reciever_name'], ENT_QUOTES);
This is the most common bug in PHP (and in many other languages).
You should escape all the text you are displaying, especially when it comes from untrusted sources - and every value provided by the user is untrusted.
Failing to escape the output you risk the security of your users - you may want to read about Cross-site-scripting on Wikipedia.
The following PHP code
echo $msg_sender_name = $info[reciever_name];
seems to be missing a couple of quotes. Try this instead:
echo $msg_sender_name = $info['reciever_name'];

Selenium, xpath to match a table row containing multiple elements

I'm trying to find a Selenium/PHP XPath for matching a table row that contains multiple elements (text, and form elements).
Example:
<table class="foo">
<tr>
<td>Car</td><td>123</td><td><input type="submit" name="s1" value="go"></td>
</tr>
</table>
This works for a single text element:
$this->isElementPresent( "//table/tbody/tr/td[contains(text(), 'Car')]" );
while this does NOT (omitting the /td locator):
$this->isElementPresent( "//table/tbody/tr[contains(text(), 'Car')]" );
and thus, this obviously won't work either for multiple elements:
$this->isElementPresent( "//table/tbody/tr[contains(text(), 'Car')][contains(text(), '123')]" );
Another way to do this would be with getTable( "xpath=//table[#class='foo'].x.y") for each and every row x, column y. Cumbersome, but it worked... mostly. It does NOT return the <input> tag! It will return an empty string for that cell :(
Any ideas?
This XPath expression:
/html/body/table[descendant::td[contains(.,'Car')]]
Note: If you know your schema, don't use a starting // operator. Use string value instead of text node (this way you get the concatenation of all descendant text nodes).
Several paths can be combined with | separator.
Tweak this:
//tr/td[contains(text(), 'Car')]/text() | //tr/td/input[#value="s1"]/#name
you might want to use
//td[contains,'Car'] and td[contains,'123']/ancestor::tr
that will select the tr that contains td which matches the two contains arguments
Try to use View Xpath Plugin in firefox, very useful plugin.
Learn more about Axes in Xpath: http://www.w3schools.com/xpath/xpath_axes.asp
Thanks to knb for some syntax hints.
This is slightly off-topic, but relevant to the search that led me here...
I had a table with [ name | value ] cells. I needed to get value from the row with 'name' preceding it.
(fake example, but every link I was looking for had the same text and no IDs - the point is that the context information was in a neighboring cell)
<table id="options"><tbody>
<tr>
<td>other</td>
<td>edit</td>
</tr>
<tr>
<td>this label</td>
<td>edit</td> <!-- I want this button -->
</tr>
<tr>
<td>other</td>
<td>edit</td>
</tr>
</tbody></table>
I could retrieve the button I wanted like this, using nested [[]] conditions:
//table[#id='options']/tbody/tr[td[contains(text(), 'this label')]]/td[2]/a
"get the "a" that is in a row that contains another cell with the text I'm looking for"
I think this sort of task might be a common case, so I'm posting it here FYI
In my problem, I had a list of products where it was identified by a unique SKU/catalog combination. If I wanted to add that product to a cart, I chose it by SKU and catalog.
Using foob.ar's example:
//table[#class='foo']/tr[td[contains(text(), 'Car')] and td[contains(., '123')]]
You can combine it with dman's solution for choosing a specific element/column within that row
//table[#class='foo']/tr[td[contains(text(), 'Car')] and td[contains(., '123')]]//input[#name='s1']
Edit:
The solution above works if I was only looking for those two values in any of the columns. If you want to find a value relative to a specific column, I had to modify it a bit
//table[#class='foo']/tr[td[position()=1 and contains(text(), 'Car')] and td[position()=2 and contains(text(), '123')]]//input[#name='s1']

Categories