RobotFramework: xpath's href seen as "not a valid XPath expression." - xpath

Line 1 there does find the selector
Wait Until Element Is visible xpath=//a[contains(text(),'Download selected certificate')]
Then when i try to get the href element
${url}= Get Element Attribute xpath=//a[contains(text(),'Download selected certificate')]/#href
It fails
Error says
InvalidSelectorException: Message: invalid selector: Unable to locate an element with the xpath expression //a[contains(text(),'Download selected certificate')]/ because of the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '//a[contains(text(),'Download selected certificate')]/' is not a valid XPath expression.
Im not sure why as i have a working unrelated example with similar syntax
${url}= Get Element Attribute xpath=//tr[contains(b/span, Jul)][${row_number}]/td[5]/span/a/#href

In xpath, a value of //a[contains(text(),'Download selected certificate')]/#href would return an object which is the href attribute of that a tag.
In Selenium2Library though, when calling Get Element Attribute, # designates the attribute name to get the value of, for a particular dom elelement.
So what that keyword does is to split that string on the last #, get a webelement on the 1st part, and retrieve the target attribute's (the 2nd part of the split) value.
Doing so, it ends up with a locator for the element being //a[contains(text(),'Download selected certificate')]/ - and that trailing / makes it an invalid xpath.
The solution is simple - lose the trailing slash; e.g.:
${url}= Get Element Attribute xpath=//a[contains(text(),'Download selected certificate')]#href
As for why your last sample works - it beats me too :)

Related

Why am I getting is not a valid xpath expression

I'm getting "not a valid xpath expression" after using this xpath.
(//span[contains(text(),'1h')]//following::span[contains(text(),'15m')])[1] when I inspect this element it returns back with 1 result.
I tried using different locator in same keyword 1st 2 exact xpath will be found & on the 3rd one it will fail with the error above.
Assert 'xpath=(//span[contains(text(),'${course}')])[1]' Has State Of '${state}'(finds it successully)
Assert 'xpath=(//span[text()='${hr}'])[1]' Has State Of '${state}' (finds it successully)
Assert 'xpath=(//span[text()='${min}'])[1]' Has State Of '${state}' (throws error for this one)
It is visible on the page.

Xpath Error: Can not convert #STRING to a NodeList

In one of the API solution, incoming request is in XML format, and i need to fetch first child node tag name to make decision to run the logic. I am using xpath to get the tag name, when in am running xpath i am getting error "Can not convert #STRING to a NodeList"
I have tried with local-name and name, but both are giving same error.
my xml is as below
<p:Check xmlns:p="http://amarwayx.com.cu/WCSXMLSchema/creptonium">
<AttributeChnageLocal>
<TaskID>17723</TaskID>
<BatchID>12345</BatchID>
<Expiry>2022-12-06</Expiry>
<TimeStamp>2019-07-20T22:45:48</TimeStamp>
</AttributeChnageLocal>
</p:Check>
and Xpath i used are
local-name(/p:Check/*)
name(/p:Check/*)
local-name(/p:Check/*[1])
name(/p:Check/*[1])
how ever is some online xpath evaluator has evaluated correct name(AttributeChnageLocal), i am not getting where the xpath syntax is wrong.
below is my tool snapshot.
same kind of expression works fine
You have ticked a box labelled "store the string value of the selected node as text", which suggests that the XPath evaluation tool you are using expects your XPath expression to select a node; but it doesn't, it selects a string.
I don't know what this tool you are using is, but unfortunately all its options seem to assume that you are selecting nodes.

How to search multiple locators using a single find in ruby capybara?

In Ruby/Capybara, I tried searching multiple(two) locators(css) in a single find query and found that it automatically search both of them and perform the action on the locator which is present on page.
Ex-
find("css1","css2").set "ABC"
I observed that while running the script, at run time it search for both the locators and will perform the action on the one which is present on page.
However, When I tried the same logic using xpath, it dont work and throw element not found error or invalid selector(one xpath is present on page).
ex-
find(:xpath,"xpath1","xpath2").set "ABC"
Can anyone please help how we can do it for xpath also in ruby capybara.
The example you show of find("css1","css2").set "ABC" won't actually do anything with the "css2" argument passed and, in the current version of Capybara, will actually emit a warning about unused parameters. What will work would be
find("css1, css2").set("ABC")
because it's using the grouping comma which will find items matching either css1 or css2. In XPath you can do that with the union operator | which will return elements that match xpath1 or xpath2
find(:xpath, "xpath1 | xpath2").set("ABC")

getting attribute via xpath query succesfull in browser, but not in Robot Framework

I have a certain XPATH-query which I use to get the height from a certain HTML-element which returns me perfectly the desired value when I execute it in Chrome via the XPath Helper-plugin.
//*/div[#class="BarChart"]/*[name()="svg"]/*[name()="svg"]/*[name()="g"]/*[name()="rect" and #class="bar bar1"]/#height
However, when I use the same query via the Get Element Attribute-keyword in the Robot Framework
Get Element Attribute//*/div[#class="BarChart"]/*[name()="svg"]/*[name()="svg"]/*[name()="g"]/*[name()="rect" and #class="bar bar1"]/#height
... then I got an InvalidSelectorException about this XPATH.
InvalidSelectorException: Message: u'invalid selector: Unable to locate an
element with the xpath expression `//*/div[#class="BarChart"]/*[name()="svg"]/*
[name()="svg"]/*[name()="g"]/*[name()="rect" and #class="bar bar1"]/`
So, the Robot Framework or Selenium removed the #-sign and everything after it. I thought it was an escape -problem and added and removed some slashes before the #height, but unsuccessful. I also tried to encapsulate the result of this query in the string()-command but this was also unsuccessful.
Does somebody has an idea to prevent my XPATH-query from getting broken?
It looks like you can't include the attribute axis in the XPath itself when you're using Robot. You need to retrieve the element by XPath, and then specify the attribute name outside that. It seems like the syntax is something like this:
Get Element Attribute xpath=(//*/div[#class="BarChart"]/*[name()="svg"]/*[name()="svg"]/*[name()="g"]/*[name()="rect" and #class="bar bar1"])#height
or perhaps (I've never used Robot):
Get Element Attribute xpath=(//*/div[#class="BarChart"]/*[name()="svg"]/*[name()="svg"]/*[name()="g"]/*[name()="rect" and #class="bar bar1"])[1]#height
This documentation says
attribute_locator consists of element locator followed by an # sign and attribute name, for example "element_id#class".
so I think what I've posted above is on the right track.
You are correct in your observation that the keyword seems to removes everything after the final #. More correctly, it uses the # to separate the element locator from the attribute name, and does this by splitting the string at that final # character.
No amount of escaping will solve the problem as the code isn't doing any parsing at this point. This is the exact code (as of this writing...) that performs that operation:
def _parse_attribute_locator(self, attribute_locator):
parts = attribute_locator.rpartition('#')
...
The simple solution is to drop that trailing slash, so your xpath will look like this:
//*/div[#class="BarChart"]/... and #class="bar bar1"]#height`

Selenium find element which have no attributes but have parents with same attributes

I am trying to find XPath of an element which has no attribute. It can only be identified by its parent's attribute. However, the parent also does not have unique attribute.
Eg: //*[#id="btn"][1]/ul/li[2]/a/span
Here there are 2 elements with id=btn. How do i get the 2nd element. The above syntax gives me 1st element.. However if i use:
//*[#id="btn"][2]/ul/li[2]/a/span
I get an error message
"The xpath expression '//*[#id="btn"][2]/ul/li[2]/a/span' cannot be evaluated or does not result in a WebElement "
Try this, you select those two first, then use brackets around and index them.
(//*[#id="btn"]/ul/li[2]/a/span)[2]
By the way, it's not a good practice to have multiple elements sharing same ids, if you are the developer, may consider change them.

Resources