How to define custom locating strategy for select

165 views Asked by At

I looking for a proper way to redefine/extend locating strategy for select tag in Gwt app. From html snippet you can see that select tag is not visible. So to select option from list I need to click on button tag, and than select needed li tag from dropdown.

<div class="form-group">
  <select class="bootstrap-select form-control" style="display: none; locator='gender">
    <div class="btn-group">
    <button class="dropdown-toggle" type="button" title="Male">
      <div class="dropdown-menu open">
        <ul class="dropdown-menu inner selectpicker" role="menu">
          <li data-original-index="1"> (contains a>span with option text)
            .....more options
      </ul>
    </div>
  </div>
</div>

I see dirty solution: to implement method in BasePage class. This approach nice page_object sugar(options,get value, etc):

def set_nationality(country, nationality='Nationality')
  select = button_element(xpath: "//button[@title='#{nationality}']")
  select.click
  option = span_element(xpath: "//span[.='#{country}']")
  option.when_visible
  option.click
end

Is there any other more clear way to do so? Using `PageObject::Widgets maybe?

UPD: Here what I expect to get:

def bool_list(name, identifier={:index => 0}, &block)
  define_method("#{name}_btn_element") do
    platform.send('button_for', identifier.clone + "//button")
  end
  define_method("#{name}?") do
    platform.send('button_for', identifier.clone + "//button").exists?
  end
  define_method(name) do
    return platform.select_list_value_for identifier.clone + '/select' unless block_given?
    self.send("#{name}_element").value
  end
  define_method("#{name}=") do |value|
    return platform.select_list_value_set(identifier.clone + '/select', value) unless block_given?
    self.send("#{name}_element").select(value)
  end
  define_method("#{name}_options") do
    element = self.send("#{name}_element")
    (element && element.options) ? element.options.collect(&:text) : []
  end
end 
1

There are 1 answers

0
Justin Ko On BEST ANSWER

The select list appears to have the most identify attributes, therefore I would use it as the base element of the widget. All of the other elements, ie the button and list items, would need to be located with respect to the select list. In this case, they all share the same div.form-group ancestor.

The widget could be defined as:

class BoolList < PageObject::Elements::SelectList
  def select(value)
    dropdown_toggle_element.click
    option = span_element(xpath: "./..//span[.='#{value}']")
    option.when_visible
    option.click
  end

  def dropdown_toggle_element
    button_element(xpath: './../div/button')
  end

  def self.accessor_methods(widget, name)
    widget.send('define_method', "#{name}_btn_element") do
      self.send("#{name}_element").dropdown_toggle_element
    end

    widget.send('define_method', "#{name}?") do
      self.send("#{name}_btn_element").exists?
    end

    widget.send('define_method', name) do
      self.send("#{name}_element").value
    end

    widget.send('define_method', "#{name}=") do |value|
      self.send("#{name}_element").select(value)
    end

    widget.send('define_method', "#{name}_options") do
      # Since the element is not displayed, we need to check the inner HTML
      element = self.send("#{name}_element")
      (element && element.options) ? element.options.map { |o| o.element.inner_html } : []
    end
  end
end
PageObject.register_widget :bool_list, BoolList, :select

Notice that all locators are in relation to the select list. As well, notice that we use the accessor_methods to add the extra methods to the page object.

The page object would then use the bool_list accessor method. Note that the identifier is for locating the select element, which we said would be the base element of the widget.

class MyPage
  include PageObject

  bool_list(:gender, title: 'Gender')
  bool_list(:nationality, title: 'Nationality')
end

The page will now be able to call the following methods:

page.gender_btn_element.click
page.gender_btn_element.exists?
page.gender
page.gender = 'Female'
page.gender_options

page.nationality_btn_element.click
page.nationality_btn_element.exists?
page.nationality
page.nationality = 'Barbados'
page.nationality_options