Simplifying tests for Gxt and Gwt applications
I have simplified the creation of widgets for the PageObject gem using the module shown below. When testing with Watir or Selenium and Cheezy’s PageObject gem, this module allows for the easy creation of test widgets, primarily used for interacting with Gwt or Gxt web applications.
For example, a Gxt table consists of multiple div elements, and each row is itself a table element containing a single row. To interact with the Gxt table, I extend the PageObject::Element::Table class:
class GxtTable < PageObject::Elements::Table
@protected
def child_xpath
".//descendant::tr"
end
end
I then register the class with the PageObject gem. The register_widget method accepts a tag which will be used as the accessor, the class which will be added to the Elements module, and a html element tag which will be used as the top-level html element of the widget, or the html element used in the watir or selenium search.
Widgets.register_widget :gxt_table, GxtTable, :div
The GxtTable then behaves as if it were an html element for the purpose of page-object definitions.
class WidgetTestPageObject
include PageObject
gxt_table(:a_table, :id => "top_div_id")
gxt_table :gxt_block_table do |element|
"block_gxt_table"
end
div(:outer_div)
gxt_table(:a_nested_gxt_table) {|page| page.outer_div_element.gxt_table_element}
end
The code for the WidgetModule is shown below. My next step is to submit this code as a pull request within PageObject.
require 'page-object/elements'
require 'page-object/platforms/selenium_webdriver/page_object'
require 'page-object/platforms/watir_webdriver/page_object'
module Widgets
def self.register_widget(widget_tag, widget_class, base_element_tag)
if widget_class.ancestors.include? PageObject::Elements::Element
define_accessors(PageObject::Accessors, widget_tag)
define_nested_elements(PageObject::Elements::Element, widget_tag)
define_locators(PageObject, widget_tag)
define_selenium_accessors(PageObject::Platforms::SeleniumWebDriver::PageObject, widget_tag, widget_class, base_element_tag)
define_watir_accessors(PageObject::Platforms::WatirWebDriver::PageObject, widget_tag, widget_class, base_element_tag)
end
end
@private
def self.define_accessors(base, widget_tag)
accessors_module = Module.new do
class_eval "def #{widget_tag}(name, identifier={}, &block)
identifier={:index=>0} if identifier.empty?
define_method(\"\#{name}_element\") do
return call_block(&block) if block_given?
platform.#{widget_tag}_for(identifier.clone)
end
define_method(\"\#{name}?\") do
return call_block(&block).exists? if block_given?
platform.#{widget_tag}_for(identifier.clone).exists?
end
end"
end
base.send(:include, accessors_module)
end
def self.define_watir_accessors(base, widget_tag, widget_class, base_element_tag)
base.send(:define_method, "#{widget_tag}_for") do |identifier|
find_watir_element("#{base_element_tag}(identifier)", widget_class, identifier, base_element_tag)
end
base.send(:define_method, "#{widget_tag}s_for") do |identifier|
find_watir_elements("#{base_element_tag}(identifier)", widget_class, identifier, base_element_tag)
end
end
def self.define_selenium_accessors(base, widget_tag, widget_class, base_element_tag)
base.send(:define_method, "#{widget_tag}_for") do |identifier|
find_selenium_element(identifier, widget_class, base_element_tag)
end
base.send(:define_method, "#{widget_tag}s_for") do |identifier|
find_selenium_elements(identifier, widget_class, base_element_tag)
end
end
def self.define_nested_elements(base, widget_tag)
base.send(:define_method, "#{widget_tag}_element") do |identifier={}|
identifier={:index => 0} if identifier.empty?
@platform.send("#{widget_tag}_for", identifier.clone)
end
base.send(:define_method, "#{widget_tag}_elements") do |identifier={}|
identifier={:index => 0} if identifier.empty?
@platform.send("#{widget_tag}s_for", identifier.clone)
end
end
def self.define_locators(base, widget_tag)
base.send(:define_method, "#{widget_tag}_element") do |identifier={}|
identifier={:index => 0} if identifier.empty?
platform.send("#{widget_tag}_for", identifier.clone)
end
base.send(:define_method, "#{widget_tag}_elements") do |identifier={}|
platform.send("#{widget_tag}s_for", identifier.clone)
end
end
end
I’d be interested in seeing actually real-world implementation of this!
My fork of PageObject on GitHub has all the tests for this module including a cucumber feature which uses the GxtTable shown to test a gxt examples page. Is there a particular example that would help?