gem 'watir', '>=1.6.2' require 'watir' require 'watir/ie' module Watir # for firefox and ie module RadioCheckGroupCommonWatir # size or count of controls in a group def size @o.size end alias count size # sets control in a group by either position in a group # or by hidden value attribute def set(what) if what.kind_of?(Array) what.each {|thing| set thing } #calls itself with Fixnum or String else if what.kind_of?(Fixnum) get_by_position(what).set elsif what.kind_of?(String) get_by_value(what).set else raise ::Watir::Exception::WatirException, "argument error #{what} not allowed" end end end # returns array of value attributes def values raise ::Watir::Exception::WatirException, "method should be implemented" end # returns Radio||Checkbox from a group that # has specific value attribute def get_by_value value raise ::Watir::Exception::WatirException, "method should be implemented" end # returns Radio||Checkbox from a group that # occupies specifi position in a group # WARNING: it is 1-based NOT 0-based # the intention is to enumerate position staring with 1, the way # customer would enumerate items def get_by_position position if (1..self.size).member? position @o[position-1] else raise ::Watir::Exception::WatirException, "positon #{position} is out of range of size" end end end # for IE only module RadioCheckGroup def values opts = [] @o.each {|rc| opts << rc.ole_object.invoke('value')} return opts end def get_by_value value if values.member? value @o.find {|rc| rc.ole_object.invoke('value') == value} else raise ::Watir::Exception::WatirException, "value #{value} not found in hidden_values" end end end #for firefox and ie module RadioGroupCommonWatir # Only one radio in RadioGroup can be selected just like # only one option in single select list box can be selected. # this method is a bit gratuitious because it will always return array # with one item but it's here to keep the plural for compatibility with # CheckboxGroup or SelectList. if at some point your page object gets changed from RadioGroup # to SelectList your tests will not have to change def selected_values selected_value.to_a end # returns radio that is selected. # there can only be one radio selected. # in the event that none is selected it returns nil # see selected_value commentary def selected_radio @o.find {|r| r.isSet?} end # if a radio button in a group is set then the group is set # by default it should be set but many HTML implementations provide # the radiogroup to the user with no default one set (Bad practice perhaps) def set? selected_radio ? true : false end end # radios that share the same :name attribute form a RadioGroup. # RadioGroup semantically behaves like single select list box # usage: this class is accessed by Watir::Container#radio_group # RadioGroup semantically behaves like single select list box. # # per HTML401: - # "If no radio button in a set sharing the same control name # is initially 'on', user agent behavior for choosing which # control is initially 'on' is undefined # # The idea of having all radios off makes no sense but in the wild you can see lots of examples. # it would be better to just have a single select list box with no items selected instead of radios. # The point of having radios is that at least one radio is 'ON' providing a default value for the group # # @browser = Watir::IE.attach :url, // # @browser.radio_group('food') # => RadioGroup with :name, 'food' # class RadioGroup include RadioCheckGroupCommonWatir include RadioCheckGroup include RadioGroupCommonWatir def initialize(container, how, what) @container = container @how = how @what = what @o = locate end def name @name end def locate @name = case @how when :name then @what when :index then names = [] @container.radios.each do |r| names << r.name end names.uniq.at(@what-1) # follow 1-based index addressing for Watir API end @container.radios.find_all {|r| r.name == @name} end private :locate # which value is selected?. returns value text as string # So per HTML401 spec I am not sure if we should ever have empyt array returned here # if you do get empty array then I would speak with developers to fix this and explicity # provide checked for one radio on page load. def selected_value selected_radio.ole_object.invoke('value') end # in the absence of visible text like in select list we treat value # as a selected text invisible to the user alias selected selected_value end class TextFields < ElementCollections def reflect ret = [] self.each do |item| how, what = get_how_what get_attribs(item) facename = suggest_def_name what value = item.value # this approach relies on doc element ret << "face(:#{facename}) {doc.text_field(:#{how}, #{what.inspect})}" ret << "#{facename}.value.should == #{value.inspect}" end ret end end class RadioGroups < ElementCollections def element_class; RadioGroup; end def length names = [] @container.radios.each do |r| names << r.name end names.uniq.size #non repeating names end def reflect ret = [] self.each do |item| name = item.name facename = suggest_def_name name values = item.values selected = item.selected ret << "face(:#{facename}) {doc.radio_group(#{name.inspect})}" ret << "#{facename}.values.should == #{values.inspect}" ret << "#{facename}.selected.should == #{selected.inspect}" end ret end private def iterator_object(i) @container.radio_group(:index, i + 1) end end module CheckboxGroupCommonWatir # returns selected checkboxes as array # when empty [] then nothing is selected # when [checkbox, checkbox] = array of checkboxes that are selected # that you can iterate over for tests. def selected_checkboxes @o.select {|cb| cb.isSet?} end # convenience method as a filter for selected_values # returns: # nil => when no checkbox is set # 'value' => if one checkbox is set # or bypass filter and return selected_values array def selected_value arr = selected_values case arr.size when 0 then nil when 1 then arr[0] else arr end end # in case of checkbox there are no visible text items. # We rely on value attributes that must be present # to differentiate the checkbox in a group # compare to SelectList where selected returns selected_item alias selected selected_value # if at least one checkbox is selected then the group is considered set def set? (selected_checkboxes != []) ? true : false end alias checked? set? end # Checkbox group semantically behaves like multi select list box. # each checkbox is a menu item groupped by the common attribute :name # each checkbox can be off initially (a bit different semantics than RadioGroup) class CheckboxGroup include RadioCheckGroupCommonWatir include RadioCheckGroup include CheckboxGroupCommonWatir def initialize(container, how, what) @container = container @how = how @what = what @o = locate end def name @name end def locate @name = case @how when :name then @what when :index then names = [] @container.checkboxes.each do |cb| names << cb.name end names.uniq.at(@what-1) # follow 1-based index addressing for Watir API end @container.checkboxes.find_all {|cb| cb.name == @name} end private :locate # returns array of value attributes. Each Checkbox in a group # has a value which is invisible to the user def selected_values values = [] selected_checkboxes.each do |cb| values << cb.ole_object.invoke('value') end return values end end class CheckboxGroups < ElementCollections def element_class; CheckboxGroup; end def length names = [] @container.checkboxes.each do |cb| names << cb.name end names.uniq.size #non repeating names end def reflect ret = [] self.each do |item| name = item.name facename = suggest_def_name(name) values = item.values selected = item.selected ret << "face(:#{facename}) {doc.checkbox_group(#{name.inspect})}" ret << "#{facename}.values.should == #{values.inspect}" ret << "#{facename}.selected.should == #{selected.inspect}" end ret end private def iterator_object(i) @container.checkbox_group(:index, i + 1) end end module Container def radio_group(how, what=nil) how, what = process_default :name, how, what RadioGroup.new(self, how, what) end def radio_groups RadioGroups.new(self) end def checkbox_group(how, what=nil) how, what = process_default :name, how, what CheckboxGroup.new(self, how, what) end def checkbox_groups CheckboxGroups.new(self) end end class RadioCheckCommon alias set? isSet? end # these methods work for IE and for Firefox module SelectListCommonWatir # selected_items examples # [] => nothing selected # ['item'] => if one selected # ['item1', 'item2', 'item3'] => several items selected def selected_items getSelectedItems end # selected_item is a convenience filter for selected_items # returns # nil if no options selected # 'text' string if one option selected. # or selected_items if more than one option selected def selected_item arr = selected_items # limit to one mehtod call case arr.size when 0 then nil when 1 then arr[0] else arr end end # for selecte lists by default we return the text of an option # compare to selected in RadioGroup or Checkbox group which return the # value attributes since there is no visible text for the user alias selected selected_item # set :value or :text def _set(how, what) if what.kind_of? Array what.each { |item| _set(how,item)} # call self with individual item else if what.kind_of? Fixnum # if by position then translate to set by text if (0..items.size).member? what _set :text, items[what-1] else raise ::Watir::Exception::WatirException, "number #{item} is out of range of item count" end else select_item_in_select_list(how, what) #finally as :value or :text end end end private :_set # similar to selected_items but returns array of option value attributes def selected_values assert_exists arr = [] @o.each do |thisItem| if thisItem.selected arr << thisItem.value end end return arr end # convinience method as a filter for select_values # returns: # nil for nothing selected. # single value if only once selected or just # or returns selected_values def selected_value arr = selected_values case arr.size when 0 then nil when 1 then arr[0] else arr end end end # SelectList acts like RadioGroup or CheckboxGroup # They all have options to select # There are two kinds of SelectLists. SingleSelect and MultiSelect # SelectList presents user with visible items to select from. # Each Item has a visible :text and invisible :value attributes # (sometimes :value attributes are missing) # # In Watirloo # The invisible :value attributes of options we call :values # The visible :text of options we call :items # The selected items as visible text we call :selected # The selected items as values we call :selected_values # # example of single select list # # # # items => ['', 'item1', 'item2'] # values => ['opt0','opt1', 'opt2'] # selected => ['item2'] # selected_values => ['opt2'] # # example of multi select list # # # # items => ['item1', 'item2', 'item3'] # values => ['o1','o2','o3'] # selected => ['item2', 'item3'] # selected_values => ['o2', 'o3'] # class SelectList include SelectListCommonWatir # accepts one text item or array of text items. if array then sets one after another. # For single select lists the last item in array wins # # examples # select_list.set 'bla' # => single option text # select_list.set ['bla','foo','gugu'] # => set 3 options by text. If # this is a single select list box it will set each value in turn # select_list set 1 # => set the first option in a list # select_list.set [1,3,5] => set the first, third and fith options def set(item) _set(:text, item) end # set item by the option value attribute. if array then set one after anohter. # see examples in set method def set_value(value) _set(:value, value) end # returns array of value attributes # each option usually has a value attribute # which is hidden to the person viewing the page def values a = [] attribute_value('options').each do |item| a << item.value end return a end alias clear clearSelection # alias, items or contents return the same visible text items alias items getAllContents def reflect ret = [] self.each do |item| name = item.name facename = suggest_def_name name values = item.values items = item.items selected_item = item.selected_item selected_value = item.selected_value ret << "face(:#{facename}) {doc.select_list(:name, #{name.inspect})}" ret << "#{facename}.items.should == #{items.inspect}" ret << "#{facename}.values.should == #{values.inspect}" ret << "#{facename}.selected_item.should == #{selected_item.inspect}" ret << "#{facename}.selected_value.should == #{selected_value.inspect}" end ret end end end