Why can I not locate a button using XPath? - xpath

I have the following HTML:
<input type="button" value="Close List" class="tiny round success button" id="btnSaveCloseListPanel">
The following code does not work:
# browser.button(:value => "Close List").click # does not work - timeout
browser.button(:xpath => "/html/body/center/div/div[9]/div[2]/input[2]").when_present.click
The error is:
Watir::Wait::TimeoutError:
timed out after 60 seconds
when_present(300) does not work.
I found the XPath using Firefox Developer Tools. I used the complete path to avoid any silly errors. I can find the same path manually in IE.
The component is a .NET MVC popup. I think it's called a "panel". The panel is a grandchild of the Internet Explorer tab.
The panel contains a datepicker, a dropdown, a text box, and 3 buttons. I can't find any of these using Watir. I can find anything in the panel's parent (obviously).
The underlying code does not seem to be aware that something actually doesn't exist. To prove that, I tested the following XPath, which is simply the above XPath with the middle bit removed:
browser.button(:xpath => "/html/body/center/div/input[2]").when_present.click
The error is "timeout", rather than "doesn't exist".
So, the code seems to be unaware that:
input[1] does not exist, therefore input[2] cannot exist.
div[2] does not exist.
Therefore there's nothing left to search.
Added:
I'm changing the specific element that I want to find.
Reason: The button in my OP was at the foot of the panel. I was going cross-eyed trying to step upwards through hundreds of lines of HTML. Instead, I'm now using the first field in the panel. All the previous info is still the same.
The first field is a text field with datepicker.
The HTML is:
<input type="text" value="" style="width:82px!important;" readonly="readonly" name="ListDateClosed" id="ListDateClosed" class="hasDatepicker">
Using F12 in Firefox, the XPath is:
/html/body/center/div/div[1]/div[2]/input
But, now, with a lot less lines of HTML, I can clearly see that the html tag is not the topmost html tag in the file. The parent of html is iframe
I've never used iframe before. Maybe this is what t0mppa was referring to in his comment to the first questiion.
As an experiment, I modified my XPath to:
browser.text_field(:xpath, '//iframe/html/body/center/div/div[1]/div[2]/input').when_present.set("01-Aug-2014")
But this times out, even with a 3-minute timeout.

Given that the elements are in an iframe, there are two things to note:
Unlike other elements types, you must always tell Watir when an element is in an iframe.
XPaths (in the context of Watir) cannot be used to cross into frames.
Assuming that there is only 1 iframe on the page, you can explicitly tell Watir to search the first iframe by using the iframe method:
browser.iframe.text_field(:xpath, '//body/center/div/div[1]/div[2]/input').when_present.set("01-Aug-2014")
If there are multiple iframes, you can use the usual locators to be more specific about which iframe. For example, if the iframe had an id:
browser.iframe(id: 'iframe_id')
.text_field(xpath: '//body/center/div/div[1]/div[2]/input')
.when_present
.set("01-Aug-2014")

Related

I need to scrape that number xpath + aspx

I'm trying to get the total number of the page (or the page value) of this URL:
http://scorelibrary.fabermusic.com/String-Quartet-No-2-300-Weihnachtslieder-23032.aspx
1/58
I think that I can't because the values are inside in the ASPX frame.
I'll try a lot of thing. This is the line:
<label id="page_count">1/58</label>
using the following XPath
//label[#id='page_count']/text()
How can I use XPath inside the ASPX frame to get the page value?
You are right, you cannot get that value directly because the element is in an <iframe>, and therefore lives in a different context. You need to activate or switch to the URL context of the iframe. There are JavaScript approaches like postMessage, but I think the easiest way is loading the URL of the iframe directly and access the DOM from there.

Behat/Mink - trouble finding buttons

My application under test has been developed by external suppliers so I have no control over the HTML structure. The application is extremely Javascript and Ajax heavy, with numerous dynamically generated buttons and auto-complete lists.
In other words, the characteristics of the pages are that they are filled with:
Elements with no fixed IDs (IDs are generated on the fly and have
numbers or other text dynamically added to them)
The same happens with some classes
Most of the times the buttons have no text associated with them since they are either custom coded 'down' arrows for lookup lists
(which aren't lookup lists but hidden divs) or '+' and '-' icons to
maximise or minimise portions of the content. -
It is therefore very difficult to identify these elements, especially the buttons.
I am trying to write a generic 'I click on the button near y' type of step so that it is not necessary to hardcode each and every button (assuming I can even get something to identify them with) into each and every test.
The thinking behind this is that normally there is a label of some sort close to the button at least.
What I want to to is to find the text label, then see if there is a button inside the same scope, and if there is not, move 'back' through the parent elements, and check if there is a button inside the scope of each parent level, up to 5 parents.
There might be all sorts of problems with this approach but I am just curious to see if this will work in general. I have run into some problems.
First I tried to use Xpaths, so I got the Xpath of the parent through :
$parentelement = $element->getParent();
$parentXpath->getXpath();
This would give me an Xpath of : (//html//span[text()='Cost center'])[1] and moving up through the parent elements all the time, they would become successively:
(//html//span[text()='Cost center'])[1]/..[1]
(//html//span[text()='Cost center'])[1]/..[1]/..[1]
and so forth.
The actual button is located in: (//html//span[text()='Cost center'])[1]/..[1]/..[1]//button but it has to go through all the parent elements in order to get there, so it will start with (//html//span[text()='Cost center'])[1]//button and should end with (//html//span[text()='Cost center'])[1]/..[1]/..[1]//button where it should find the button.
Trying to use Xpath I used:
$button_element = $session->getPage()->find('xpath',$parentXpath."//button")
I soon saw that the 'find' command appends an //html to the front of your xpath string so the Xpath that it tried to use ended up being (for each parent Xpath, but using this one as an example):
(//html(//html//span[text()='Cost center'])[1]/..[1])
I then stripped out the brackets as well as the //html, leaving me with:
//span[text()='Cost center'][1]/..[1]
but when I tried:
$button_element = $session->getPage()->find('xpath',$strippedParentXpath."//button")
I got the following error:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string '(//html//span[text()='Cost center'][1]/..[1]//button)[1]' is not a valid XPath expression
However, Firepath can execute this expression and does not show a syntax error for it, although it does not find the actual button (since the button is actually located one level up, where Firepath DOES find it).
So my question 1 is: What is wrong with my Xpath that I can't use it in the find? It actually looks as if //span[text()='Cost center'][1]//button does not throw the same exception, since as I said, I am looping through the parent Xpaths, and it starts with //span[text()='Cost center'][1]//button. It crashes on //span[text()='Cost center'][1]/..[1]//button.
My second option was to get the parent element each time, starting with finding the text on the page, but then to search for a button inside the scope of the parent element using the findbutton functionality.
Looping through the parent elements (up to a maximum of 5):
$parentelement = $parentelement->getParent();
$butonelement = $parentelement->findbutton('xxx');
In other words, find ANY button in the scope of the parent element. The problem I have is how to specify a generic 'button'.
One has to associate SOME text with the button (depicted by the 'xxx' above).
But this is a typical example of buttons in the application:
<button class="autocomplete_button" type="button" id="button_OM_1"> </button>
Where the class is used more than once, and the ID is auto-generated and not the same number all the time. There is no text associated with the button since the class specifies an image.
Question 2: So how can I use 'findbutton' to generically find a 'button' no specific distinguishing characteristics? Please note that I actually did try findbutton("button"), taking the chance that there might be a 'button' somewhere in a button, but this did not work either. At least, it doesn't work consistently and by that I mean that the same test randomly seems to either find or not find the same button when I run the test a couple of times.
After doing some more investigation on this issue I have found the following:
My method of trying to find the closest button to a piece of text via traversing 'up' through the scope of the divs and spans around the text (using xpath) is actually working.
What is NOT working is SAHI, which I am using as the web driver. In other words, it is not a Behat/Mink problem, it is SAHI specific issue.
I tried the same code using Selenium2 and it executes perfectly.
I still require an answer to question 2 - how can I use findbutton() without a specific parameter such as the ID, name or value but I will see if I can find an answer to that question separately and on the Behat user group since I do think that is a Behat/Mink specific issue.
I normaly use css selector and with that, I use to navigate to the class and ID's that the button is inside. it is easier than xpath I think, like you can use
$this->getSession ()->getPage ()->find ( 'css', '.parrent1 .parrent2 .autocomplete_button ' );
I think this will help you as you know which button your gonna use in each scenario

watir, element not currently visible

I'm trying to interact with a widget on a webpage, enter data into a form element. But I'm getting an error saying the elements isn't currently visible so cannot be interacted with. When manually interacting with the page, the text box asks you to enter something, then that text goes away when you click on the box and accepts input. I've tried having watir click the box first but that isn't working for me either. Here is the raw html:
<input class="symbol-search ui-autocomplete-input x-defaulttext-ghost" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">`
after I click on the box in my browser that changes to this:
<input class="symbol-search ui-autocomplete-input" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">`
Suggestions? Not possible?
One potential problem is that the input element in your code lacks a 'type' attribute, which is what watir uses to distinguish things like text_field from checkbox (since they are all input elements, but act differently)
Without that type attribute (role doesn't really cut it, it's not even a standard element for an input field, not even in html5, not as far as I can tell
Lacking a type attribute, you won't be able to use most of the standard input types supported by watir, which means you won't have access to a .set method You are likely going to have to address this with the basic 'element' class, and then use something like .send_keys once it has been clicked in order to fire keystrokes at it.
Who's widget is this anyway? do they have a demo page that shows an example of it? that would make it easier for people to discover potential ways to work with the control.
This worked for me:
b.text_field(:class => /autocomplete/).set 'hello'

Selenium IDE - How to record xpath in the Firefox plugin

I'm having trouble with my xpaths in the Firefox plugin. I have three textboxes, the first one has ID=login and the rest has dynamically generated IDs. The first one works fine to write in the plugin, //input[#id='login'] but as soon as I try something more advanced, it cannot find anything. After reading plenty of forum posts, I've tried the XPather plugin to generate the xpath codes but the long html/css-filled strings don't work either. In some threads, people write "xpath=//..." and I tried that too, to no result.
Thankful for all help possible!
//M
The two tools I use are Firebug and XPath Checker, both Firefox add-ons.
In Firebug, select the HTML tab, right click on the element and select "Copy XPath"
For XPath Checker, right click on the element in the page and select "View XPath".
You can check this by pasting the result into the target field of Selenium IDE and then clicking the "Find" button. This tells what the Selenium result is going to be (much faster than having to run the test over and over!)
I've found that you have to "massage" the result somewhat e.g.
If you go to the Google home page and look for the "Images" link at the top left, the XPath Checker gives the image XPath as:
//id('gbar')/x:nobr/x:a1
This throws an error:
[error] locator not found: //id('gbar')/x:nobr/x:a1, error = Error: Invalid xpath 2: //id('gbar')/x:nobr/x:a1
If you remove the id e.g
//x:nobr/x:a1
you'll notice that the image has a green box around it showing thet the IDE has correctly parsed it.
Alright, I found some new information and it does work with writing "xpath=/" and the auto-generated codes from XPather. Although, the xpaths are very long and rely a lot on the HTML tags staying the same.
Does anyone have a better idea for xpath expressions (or css expressions) to write in order to enter information in the following three textbox elements:
TEXTBOX 1
Firebug: input type="text" tabindex="110"
name="6011300f91d9f186d1b7ab1a034827da"
id="input1"
Working xpath in Selenium, retrieved from XPather:
xpath=/html/body[#id='fire']/form[#id='loginForm']/div/div/div[#id='container']/div[#id='content']/div[#id='contentPadding']/div[1]/fieldset[1]/input[#id='input1']
TEXTBOX 2
input type="password" tabindex="110"
name="0de4766295b7a965fc4969da3df6824ba"
xpath=/html/body[#id='fire']/form[#id='loginForm']/div/div/div[#id='container']/div[#id='content']/div[#id='contentPadding']/div[1]/fieldset[2]/input
TEXTBOX 3
input type="password" tabindex="110" class="last" name="6c4e0fcde65e258c3dc7c508a1cc666a"
xpath=/html/body[#id='fire']/form[#id='loginForm']/div/div/div[#id='container']/div[#id='content']/div[#id='contentPadding']/div[1]/fieldset[3]/input
//M

XPath Expression

I am new to XPath. I have a html source of the webpage
http://london.craigslist.co.uk/com/1233708939.html
Now I want to extract the following data from the above page
Full Date
Email - just below the date
I also want to find the existence of the button "Reply to this post" on the page
http://sfbay.craigslist.org/sfc/w4w/1391399758.html
Can anyone help me in writing the three XPath expressions for the above three data.
You don't need to write these yourself, or even figure them out yourself. If you use the Firebug plugin, go to the page, right click on the elements you want, click 'Inspect element' and Firebug will popup the HTML in a viewer at the bottom of your browser. Right click on the desired element in the HTML viewer and click on 'Copy XPath'.
That said, the XPath expression you're looking for (for #3) is:
/html/body/div[4]/form/button
...obtained via the method described above.
I noticed that the DTD is HTML 4/01 Transitional and not XHTML for the first link, so there's no guarantee that this is a valid XML document, and it may not be loaded correctly by an XML parser. In fact, I see several tags that aren't properly closed (i.e. <hr>, etc)
I don't know the first one off hand, and the third one was just answered by Alex, but the second one is /html/body/a[0].
As of your first page it's just impossible to do because this is not the way xpath works. In order for an xpath expression to select something that "something" must be a node (ie an element)
The second page is fairly easy, but you need an "id" attribute in order to do that (or anything that can make sure your button is unique). For example if you are sure the text "Reply to this post" correctly identify the button just do it with
//button["Reply to this post"]

Resources