How to locate, using Xpath, an input field without using its id - xpath

I'm working with a web page with the following HTML where I want to identify the first <input> field inside the <span> using a text_field from within a page-object.
<div id="131:"> Please enter your name:
<span class="autocompspan " style="position:static;">
<input style="position: static;" class="autocompinput yui-ac-input" id="132:" name="132:"
onfocus="juic.fire("132:","_focus",event);"
onchange="juic.fire("132:","_despatchChange",event);"
onblur="juic.fire("132:","_blur",event);" size="60"
onkeydown="juic.fire("132:","_onkeydown",event);"
onkeyup="juic.fire("132:","_onkeyup",event);" aria-disabled="false" value=""
role="combobox" aria-autocomplete="list" aria-owns="132:_divList"
aria-activedescendant="132:_divAD" findtype="proxy" delimchar="" hideusername="false"
fetchusername="false" autocomplete="off" type="text">
<input value="" id="132:_hidden" name="132:_hidden" type="hidden">
</span>
</div>
If I use :id => '132:' to identify the field things work fine. I.e. text_field(:target_user_name, :id => '132:' ) works.
The issue is that this HTML is generated by the underlying app (SAP) which does not always generated the same value for the <input> field id so using the id cannot be relied upon to consistently identify the element.
So, given the above HTML what other ways might I go about reliably finding this <input> field.
I've tried the following, none of which work. Most of them time out waiting for the element to be located.
text_field(:target_user_name, :xpath => "//*[#class='autocompinput yui-ac-input' and #role = 'combobox']" )
text_field(:target_user_name, :xpath => "//*[#class='autocompinput' and #role = 'combobox']" )
text_field(:target_user_name, :xpath => "//span/input[#class='autocompinput yui-ac-input' and #role = 'combobox']" )
text_field(:target_user_name, :xpath => "//input[#class='autocompinput yui-ac-input' and #role = 'combobox']" )
text_field(:target_user_name, :class => 'autocompinput yui-ac-input')
Any thoughts?

When an element does not have unique identifiable attributes, you should look at the elements around it. In this case, there is user visible text that helps a user identify the purpose of the field. That same text can be used to identify the element in Watir.
As the surrounding div only contains the labelling text, you can search for that div by its text and get the only text field in it:
browser.div(text: 'Please enter your name:').text_field
As a page-object accessor:
text_field(:target_user_name) { div_element(text: 'Please enter your name:').text_field_element }

Firstly, Watir is designed to make it so that you shouldn't have to use XPATH.
It depends on how many different elements/ids are on the page, but I've found that using regular expressions often works well with dynamically generated ids. So either grab the id and use it elsewhere:
id = browser.text_field(id: /\d\d\d/).tr(':', '')
or just use it directly:
text_field(:target_user_name, id: /\d\d\d:/)

In this particular case you can check the first input field after 'Please enter your name:' text using below xpath:
//div[text()='Please enter your name:']//following::input[1]
In general if you encounter fields that does not have a unique identifier you can rely on static text or fields and then use xpath functions such as following, preceding etc.

Related

How to locating similar HTML elements in Watir using Ruby

I am trying to click links on a page and able to do only the first one. There are four more having similar code, but it says it cannot locate the other four.
This is the line of code that works:
#browser.div(class:'ms-vb itx').link(:text =>'Rapid Alignment').click
This is one of the four that does not work:
#browser.div(class:'ms-vb itx').link(:text =>'Design Develop Integrate and Test').click
HTML:
<div class="ms-vb itx" onmouseover="OnItem(this)" CTXName="ctx586" id="1" Field="LinkTitle" Perm="0xb008031061" EventType=""><a onfocus="OnLink(this)" href="asdm.nwie.net/_layouts/15/…; onclick="EditLink2(this,586);return false;" target="_self">Rapid Alignment</a></div>
<div class="ms-vb itx" onmouseover="OnItem(this)" CTXName="ctx586" id="3" Field="LinkTitle" Perm="0xb008031061" EventType=""><a onfocus="OnLink(this)" href="asdm.nwie.net/_layouts/15/…; onclick="EditLink2(this,586);return false;" target="_self">Design Develop Integrate and Test</a></div>
I think the issue is the use of #div which will return a single div
Try this instead
divs = #browser.divs(class:'ms-vb itx')
Then
divs.each do |d|
d.link.click
end
#divs returns a DivCollection which includes Enumerable so all Enumerable methods will work as well including things like select e.g.
divs.select { |d| d.link(:text =>'Rapid Alignment') }
You'll have to specify which <div> you are targeting. There are two or possibly more <div> tags with the same class attribute.
Given this HTML snippet:
<div class="ms-vb itx" onmouseover="OnItem(this)" CTXName="ctx586" id="1" Field="LinkTitle" Perm="0xb008031061" EventType=""><a onfocus="OnLink(this)" href="asdm.nwie.net/_layouts/15/…" onclick="EditLink2(this,586);return false;" target="_self">Rapid Alignment</a></div>
<div class="ms-vb itx" onmouseover="OnItem(this)" CTXName="ctx586" id="3" Field="LinkTitle" Perm="0xb008031061" EventType=""><a onfocus="OnLink(this)" href="asdm.nwie.net/_layouts/15/…" onclick="EditLink2(this,586);return false;" target="_self">Design Develop Integrate and Test</a></div>
You need to target the appropriate <div> by supplying the index in the locator:
p b.div(:class => 'ms-vb itx').link(:text => 'Rapid Alignment').exists?
#=> true
p b.div(:class => 'ms-vb itx').link(:text => 'Design Develop Integrate and Test').exists?
#=> false
p b.div(:class => 'ms-vb itx', :index => 1).link(:text => 'Design Develop Integrate and Test').exists?
#=> true
But locating elements by index can be fragile if and when UI elements change. You should consider locating using the id attributes, which--according to spec--are unique.
This fails because div is same so it tries to locate the same div everytime and starts to search the given link, So it fails second time when you tries to locate the different link.
Actually you do not need of div to locate that link, you simply write this code it will work
b.link(:text=>'Rapid Alignment',:visible=>true).click
b.link(:text=>'Design Develop Integrate and Test',:visible=>true).click
That link text itself is the identification to that link, So you do not need of any division, directly write b.link(), it's enough.

How to find a field using placeholder text in Watir?

I have two search fields and I want to find the second one to set some text.
I have tried using div however it always finds the first search field.
Does anyone one have a suggestion on how to find the second object or use the unique placeholder text "Search..." ?
HTML:
input type="text" name="searchString" id="searchString" projects="" for="" placeholder="Search" class="form-control"
input type="text" name="searchString" id="searchString" placeholder="Search..." class="form-control"
Ruby - Watir:
#b.link(:text => "http://ml-test.mytest.com/Client/Profile/ab295b41-2c5e-4100-bdee-e757405238bb").click
#b.text_field{div(:class => "col-sm-4 col-md-3", :placeholder => "Search...")}.set "Automation (Test)"
It looks like the problem might simply be a typo in the code. Notice in the line:
#b.text_field{div(:class => "col-sm-4 col-md-3", :placeholder => "Search...")}.set "Automation (Test)"
That a block, seen by the {}, is being passed to the text_field method. The text_field method does not do anything with blocks, as a result the code is really just doing:
#b.text_field.set "Automation (Test)"
As there is no locator supplied to the text field, it will input the first text field on the page.
To locate the text field based on the placeholder attribute (or any other locator), it needs to be passed as an argument instead of a block:
#b.text_field(:placeholder => "Search...").set "Automation (Test)"
You had included a div as well. Assuming that it is an ancestor element need to find the text field, it should be:
#b.div(:class => "col-sm-4 col-md-3").text_field(:placeholder => "Search...").set "Automation (Test)"

watir-webdriver not finding or filling in text_field

I have a watir-webserver Ruby script I am developing, and I am having it first prompt the user for a username and password (with Ruby-Tk).
In any case, I can open the page with this code:
b = Watir::Browser.start 'https://connect.mypage.com/Home'
After that, I call the following:
t1=b.text_field(:id => 'ctl00_cphBody_txtUserName').when_present.set #entry1.textvariable
Nothing is filled in.
I have tried hard coding a name in for ctl00_cphBody_txtUsername, but that did not work either.
Using the Inspector in Firefox, the field is constructed as following:
<input name="ctl00$cphBody$txtUsername" id="ctl00_cphBody_txtUsername" class="firstFocus" maxlength="50" type="text">
The class of firstFocus is:
<script type="text/javascript">
$(document).ready(function () {
jQuery('.firstFocus').focus();
});
</script>
The problem is that the locator is case-sensitive.
Notice that the id attribute value is "ctl00_cphBody_txtUsername" but the locator being used is "ctl00_cphBody_txtUserName". Due to the "N" not matching the "n", the element is never found, which is why the when_present times out.
Correcting the id in the locator will fix the issue:
t1=b.text_field(:id => 'ctl00_cphBody_txtUsername').when_present.set #entry1.textvariable
If I start the browser and open the page like below, I have no problems locating those two text_field's:
browser = Watir::Browser.new
browser.goto 'https://connect.mypage.com/Home'
#Then, the first item actually has a class, so I use that
t1=browser.text_field :class => 'firstFocus'
t1.set #entry1.textvariable

ruby mechanize find and fill input tag value

I need to fill the value of an input tag not surrounded by a form tag, I do not need to submit it, just fill in a value.
I have the code below:
input = my_page.at("#crn_id1")
my_page.form_with(:id => 'crn_id1').value = '90098'
input = agent.click(my_page.at('[#value="Submit Changes"]'))
puts input.content
I am getting an error but what I am trying to do is fill out the input tag which has html of:
<TD style=padding:10px; CLASS="dedefault"><p class="centeraligntext"></p>
<LABEL for=crn_id1><SPAN class="fieldlabeltextinvisible">CRN</SPAN></LABEL>
<INPUT TYPE="text" NAME="CRN_IN" SIZE="8" MAXLENGTH="5" ID="crn_id1">
</TD>
I just want to fill out the input tag which in NO way is surrounded by a form tag with a value of 09987. I have the correct html page but cant seem to set the value of the input and tag to set it and set it in mechanize.

Scrapy Xpath query to select input tag elements

I'm using Scrapy crawl spider and trying to parse output pages to select all input tag parameter as the following :
input type: must be (text or password or file)
input id: if it's not found , select [input name] instead.
I wrote a sample code for test in Scrapy shell, but it doesn't give me the exact result.
Tested site: http://testaspnet.vulnweb.com/Signup.aspx
>>> hxs.select('//input[#id] | //input[#type="text"] | /text()').extract()
[u'<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTY0MzI4NjU4Mw9kFgICAQ9kFgICAQ9kFgQCAQ8WBB4EaHJlZgUKbG9naW4uYXNweB4JaW5uZXJodG1sBQVsb2dpbmQCAw8WBB8AZB4HVmlzaWJsZWhkZHEZ3VN6SP/C2xESDN/Y3p8zhfSB">',
u'<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWWgKJ+8rsBQLStq24BwK3jsrkBALF97vxAQKozoCcCQKpzpj7DgKSnr/eCQKSnr/eCQKSntPyAgKSntPyAgKSnseJCgKSnseJCgKSnvusAwKSnvusAwKSnu/DDAKSnu/DDAKSnoPmBQKSnoPmBQKSnre9DQKSnre9DQKSnqvQBgKSnqvQBgKSnp+5AwKSnp+5AwKSnrPcDAKSnrPcDAL3pJ3FDwL3pJ3FDwL3pLGYBwL3pLGYBwL3pKU/AvekpT8C96TZ0wkC96TZ0wkC96TN9gIC96TN9gIC96ThjQoC96ThjQoC96SVoAMC96SVoAMC96SJxwwC96SJxwwC96T9rAkC96T9rAkC96SRwwIC96SRwwICyMvj6AUCyMvj6AUCyMuXjw0CyMuXjw0CyMuLogYCyMuLogYCyMu/+Q8CyMu/+Q8CyMvTnQcCyMvTnQcCyMvHMALIy8cwAsjL+9cJAsjL+9cJAsjL7+oCAsjL7+oCAsjLw9MPAsjLw9MPAsjL9/YIAsjL9/YIAq3SwZ8KAq3SwZ8KAq3S9bIDAq3S9bIDAq3S6ckMAq3S6ckMAq3SnewFAq3SnewFAq3SsYMNAq3SsYMNAq3SpaYGAq3SpaYGAq3S2foPAq3S2foPAq3SzZEHAq3SzZEHAq3SofkFAq3SofkFAq3S1Z0NAq3S1Z0NAob5pwUChvmnBQKG+dvZCQKG+dvZCaCOP7DYDQ3mNEhISrmdoTKH9Tws">',
u'<input name="tbUsername" type="text" id="tbUsername" class="Login">',
u'<input name="tbPassword" type="password" id="tbPassword" class="Login">',
u'<input type="submit" name="btnSignup" value="Sign me up" id="btnSignup">']
All input elements of type text, password or file:
//input[#type='text' or #type='password' or #type='file']
I am not sure of what condition you want on the id or name - this will get all input elements of those three types that have either an id or name:
//input[(#type='text' or #type='password' or #type='file') and (#id or #name)]
If you want to test for the id or name (if the id does not exists) equal to something (XXXX):
//input[(#type='text' or #type='password' or #type='file') and (#id='XXXX' or (not(#id) and #name='XXXX'))]
If you want to extract the id:
//input[#type='text' or #type='password' or #type='file']/#id
I don't think extracing either the id or the name if the id is not specified is possible with standard XPaths.
I don't know Scrapy, but from a purely XPath point of view, the following should satisfy the requirements you describe:
//input[(#id or #name) and (#type = 'text' or #type = 'password' or #type = 'file')]
Also, I notice you're trying to retrieve the text content of the selected nodes. This will presumably return nothing because inputs are self-closing tags and do not hold inner content.

Resources