How to get Span collection by id in Watir - ruby

Is there a method in watir to get list of all the span collection by id. I can traverse the span tags only by index and one item at a time.
i want to get the entire collection of the span tags and loop through it.
irb(main):060:0> ie.span(:id =>/ctl00_ContentPlaceHolder1_DataList1_ctl132_lblSubject/).text
=> "30 stuff"
irb(main):065:0> ie.span(:id =>/ctl00_ContentPlaceHolder1_DataList1_ctl/).exists?
=> true
irb(main):073:0> spans=ie.span(:id =>/ctl00_ContentPlaceHolder1_DataList1_ctl/ ,:index=>3)
=> #<Watir::Span: located: false; {:id=>/ctl00_ContentPlaceHolder1_DataList1_ctl/, :index=>3, :tag_name=>"span"}>
irb(main):074:0> spans.text
=> "Well, morning."
irb(main):075:0>

Yes, you can iterate. Write the following code
ie.spans(:id =>/ctl00_ContentPlaceHolder1_DataList1_ctl/).each do |span|
p span.text
end
It will print all the span's text which is matching your id.

Related

Insert value in a text field using Ruby - watir/selenium

I am trying to insert a number in the 'Mine Id' using Ruby - Watir/Selenium
Somehow the field <input> field is not being recognized as a text box and hence I am unable to enter values through the code.
The id 'inputdrs' is used multiple times on the same page.
Any suggestions how to achieve it.
The URL is this
http://www.msha.gov/drs/drshome.htm
The below don't work:
#browser.text_field(:id => /inputdrs/, :index => 2).set("3607277")
browser.text_field(:name, "inputdrs").set("3607277")
Thank you for your help
require 'watir-webdriver'
$browser = Watir::Browser.start "http://www.msha.gov/drs/drshome.htm"
a = 0
b = $browser.text_fields.length
while a < b
$browser.text_fields[a].set a
a += 1
end
This will put the value of a in each text field on this page. I REALLY drew out the loop so you can see whats going on. This isn't as dynamic as you would like it, but if the page has the same amount of text_fields you should be fine.
OR you can do something like..
$browser.text_field(:name => "MineId").select
$browser.send_keys "hello"
Problem
The html of the Mine ID field is:
<input size="8" maxlength="8" name="MineId" onclick="this.value='';" id="inputdrs" align="middle" type="number">
The line:
#browser.text_field(:id => /inputdrs/, :index => 2).set("3607277")
Will fail because it is inputting into the wrong text field. If you get all of the text fields with that id:
browser.text_fields(:id => 'inputdrs').collect(&:name)
#=> ["q", "MineId", "OperSearch", "MineName", "CntctrId", "CntCtrSearch", "Controller"]
You can see it is the second field. However, because Watir is using a 0-based index, you actually get the OperSearch field. This would have worked by using an :index => 1 instead.
The line:
browser.text_field(:name, "inputdrs").set("3607277")
Will fail because the "inputdrs" is the value of the id attribute and not the name attribute.
Solution
Given that the id attribute value is not unique for this page, you should probably not use it for locating. Instead, use something unique, such as the name attribute.
browser.text_field(:name => "MineId").set("3607277")

Why do I get invalid attribute error when I pass in a css selector?

stackoverflow,
Here's what I'm trying to do
def get_element_from_list(root, item, index)
#browser.elements(:css => root).each do |element|
if element.present?
return element.element(:css => item, :index => index)
end
end
raise Selenium::WebDriver::Error::NoSuchElementError
end
get_element_from_list('div[class*=x-combo-list]', 'x-combo-list-item', index).click
gives me Watir::Exception::MissingWayOfFindingObjectException: invalid attribute: :css
What I don't understand is if I simply do
#browser.elements(:css => 'div[class*=x-combo-list]').each do |element|
if element.present?
return element.element(:css => 'x-combo-list-item', :index => index)
end
end
basically replacing root and item with the actual strings it works without error.
I think there might be a bug that prevents locating elements with the :css and :index locator - Issue 241.
You can work around the issue by getting an element collection and then getting the element at the specific index:
return element.elements(:css => 'x-combo-list-item')[index]
(Note that I think this css-selector might be wrong. It is probably meant to be .x-combo-list-item.)
Alternatively, assuming that x-combo-list-item is actually the element's class, you could do:
return element.element(:class => 'x-combo-list-item', :index => index)

Watir method (or monkey-patch) to select span (or other) tags with custom ("data-*") attribute values equaling a string value (or matching a regex)

So this is ruby right, and while I do have a solution already, which I'll show below, its not tight. Feels like I'm using ahem "C++ iterators", if you will. Too many lines of code. Not like ruby.
Anyway, I'm wondering if there is classier way to do this:
b = Watir::Browser.new
b.goto "javascriptinjectedtablevalues.com" #not real website url:)
# desired urls in list are immediately located within <span> tags with a "class" of
#"name" plus a custom html attribute attribute of "data-bind" = "name: $data". that's it
# unless I wanted to use child-selectors which I'm not very good at
allrows = b.spans(:class => "name").each_with_index.map do |x, i|
[0, x.attribute_value("data-bind")]
end
real_row_ids = allrows.select{|i, databind| databind == "name: $data" }.map(&:first) #now I have all correct span ids
spans = real_row_ids.map {|id| b.spans(:class => "name")[id] }
Now that's a little messy in my opinion. But it leaves artifacts so I can debug and go back and stuff.
I could use this command to just grab a just the spans
spans = b.spans(:class => "name").map do |span|
[span, span.attribute_value("data-bind")]
end.select {|span, databind| databind == "name: $data"}.map(&:first)
but that still feels messy having no artifacts to show for it to use for later when trying to isolate other html tags nearby the span.
I'm hoping there is something like this pseudo code for watir:
b.spans(:class => "name").with_custom_attributes(:key => "data-bind", :value => "name: $data")
that's what I'd really like to do. superman-patching this custom method onto Watir within a rails initializer would be the optimal solution second to it already existing within Watir!
Watir already supports using data attributes for locators. You simply need to replace the dashes with underscores.
For example:
b.spans(:class => 'name', :data_bind => "name: $data")
Would match elements like:
<span class="name" data-bind="name: $data">
Similarly, you can use a regex when matching the data attribute:
b.spans(:class => 'name', :data_bind => /name/)

Check in watir if select_list has options

Now i have such watir code:
require "watir-webdriver"
browser = Watir::Browser.new :ff
browser.driver.manage.timeouts.implicit_wait = 3 #3 seconds
browser.goto "https://page***/default.aspx"
browser.select_list(:name => 'ctl00$tresc$cbListaKrajow').select_value('6')
puts "Selected country"
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').wait_until_present
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').select_value('95')
puts "Selected city"
But main trouble is that ctl00$tresc$cbListaPlacowek is loading it's options only via
ctl00$tresc$cbListaKrajow is changed. It could take 1 or 30 seconds so `browser.driver.manage.timeouts.implicit_wait = 3 #3 seconds` is not a good idea. How can i write that it try to select option unless it is present? Now i get errors like it could find such option...
Note that you could have also done:
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').option(:value => '95').wait_until_present
Just a little nicer to write.
Update:
If you want to wait for any option to appear in the select list you can do:
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').option.wait_until_present
This is technically waiting for a first option to appear. Note that depending on what you are doing, this could give you false positives. For example, if the select list is already populated with options, you trigger some event to re-populate the list, the select list would already have a first option. In this case, you might have to also check that the list of options has changed.
If you want to select an option with a value that matches any in an array, you could do:
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').options.find do |option|
somearray.include?(option.value)
end.select
Founded by myself:
Watir::Wait.while { browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').include?('95') }

How do I get an array of check boxes in haml?

I have an array of strings, called #theModels, in a routine implemented as part of a Sinatra server. These models are options for the user to select, and are obtained by the back end (the idea being, as new models are added, then the front end code should not change).
I'm using haml to render html.
How can I enumerate each element in the list of #theModels such that each element is a checkbox? And how can I obtain which checkboxes the user has selected?
I see that just putting
= #theModels
will give me the list of strings contained in #theModels, but without spacing or the like, and certainly not in checkboxes. I've found this question that appears to be similar, but my haml-fu isn't good enough to convert that into what I need.
UPDATE:
These are options associated with a file upload, such that now the code looks like:
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
- #theModelHash.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%input{:type=>"file",:name=>"file"}
%input{:type=>"submit",:value=>"Upload"}
Problem is, that puts a file upload button on each option, instead of at the end. I only want one submit button in the end; should I have two forms that both report their results when the 'Upload' button is pressed?
UPDATE2:
After a moment's thought, the above can be modified to:
Thanks!
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
- #theModelHash.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%form{:action=>"/Upload",:method=>"post",:enctype=>"multipart/form-data"}
%input{:type=>"file",:name=>"file"}
%input{:type=>"submit",:value=>"Upload"}
And that appears to do what I want.
I think you should send the content as an hash instead.
This will give you the opportunity to set initial values in the form.
The hash #params will give you the result.
E.g. {"oranges"=>"1"}
#app.haml
%form{:method => 'post', :action => "/"}
- #models.each do |key,value|
%br
%input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
=key
%input{:type => :submit, :value => "Save"}
#app.rb
require 'sinatra'
require 'haml'
get '/' do
#models = {"oranges" => true, "bananas" => false}
haml :app
end
post '/' do
#params.inspect
end
The link you provided linked to a rails solution where you have a function returning the proper html.
You can define this function yourself:
Input: key, value
Output: %input{:type=>"checkbox", :name=>"#{key}", :value=>1, :checked=>value}
def check_box(key, value)
...
end
and call it in haml with
=check_box(key,value)

Resources