Using watir-webdriver with chrome - can't locate input field? - ruby

I have the following HTML:
<input type="email" class="form-control" name="email" placeholder="Email">
And I am trying to retrieve this element with the following code:
b = Watir::Browser.new :chrome
b.goto('localhost:3000')
puts b.input(:name => "email").exists?
This returns false, but is most definitely true. I have also tried using b.text_field(:name => 'email').exists?, but it returns false as well.
The end goal is to change the text of the input, but I can't even locate the element right now. The page loads fine, and after loading it outputs false.

The issue is your code is actually being executed before the element is finished loading. You need to chain a few methods (namely .when_present and .exists? to ensure your element is loaded before attempting to check it's existence:
puts b.input(:name => "email").when_present.exists?
Good luck!

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 locate, using Xpath, an input field without using its id

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.

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)"

Interact with a file_field inside span - Watir/Selenium-Webdriver

How do I interact with a file_field thats hidden by its parent?
<span class="btn button-large fileinput-button">
Select files...
<input accept="image/png, image/gif, image/jpeg" id="gallery_files" multiple="multiple" name="gallery_files" type="file">
</span>
The button overlays the input, therefore it's not visible.
Edit
For the record, here's my code:
data[:photos].each do |photo|
$browser.file_field.set photo
end
and the error: Watir::Wait::TimeoutError: timed out after 20 seconds, waiting for {:tag_name=>"input", :type=>"file"} to become present
Workable example in a Gist
I was a bit suprised, but I was able to set the file field in the sample HTML without any issue using:
browser.file_field.set('path/to/file.txt')
From the code, it looks like setting the file field only requires the input to exist. It does not require it to be visible (or present).
Given that you are getting a Watir::Wait::TimeoutError exception, I would guess that your code is actually failing before the file_field.set. As it looks like the page has the input in a dialog, I am guessing your code actually looks more like:
$browser.file_field.wait_until_present
data[:photos].each do |photo|
$browser.file_field.set photo
end
It would be the wait_until_present method that is actually throwing the exception.
Solution 1
Assuming that an explicit wait method is being called for the file field, you should be able to just remove the wait.
If you have the wait because the dialog is being loaded by Ajax, you could try waiting for a different element instead - eg the parent span.
Solution 2
If for some reason you need the file field to be present, you will need to change its CSS. In this case, the opacity:
p $browser.file_field.present?
#=> false
$browser.execute_script('arguments[0].style.opacity = "1.0";', browser.file_field)
p $browser.file_field.present?
#=> true
For my situation, this worked:
$browser.execute_script("jQuery(function($) {
$('.fileinput-button').css('visibility', 'hidden')
$('#gallery_files').css('visibility', 'visible').css('opacity', '1').css('width', '100').css('height', '50')
})")
I had to hide the parent span, then show, resize, and change the opacity of the input

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

Resources