Class: WatirPump::Component

Inherits:
Object
  • Object
show all
Extended by:
Forwardable, WatirPump::Components::CheckboxGroup, WatirPump::Components::DropdownList, WatirPump::Components::Flag, WatirPump::Components::RadioGroup
Includes:
Constants
Defined in:
lib/watir_pump/component.rb

Overview

Representation of a reusable page component.

Next to class methods documented below it contains also dynamically generated methods for declaring Watir::Elements belonging to the component.

See WATIR_METHOD_MAPPING for a complete list of elemet methods.

There are also dynamically generated class methods that create reader, writer and clicker instance methods. Please refer to README for more details.

Examples:

class MyComponent < WatirPump::Component
  # declaration of a div element
  # instance method `description` returns Watir::Div
  div :description, id: 'desc'

  # declaration of a div element reader
  # instance method `description` returns String
  div_reader :description, id: 'desc'

  # declaration of a button element clicker
  # instance method `login` clicks on the button
  button_clicker :login, id: 'submit'

  # declaration of a text_field writer
  # instance method `surname=(value)` sets value of the text_field
  text_field_writer :surname, id: 'surname'
end

Direct Known Subclasses

Page

Constant Summary

Constants included from Constants

WatirPump::Constants::CLICKABLES, WatirPump::Constants::METHODS_FORWARDED_TO_ROOT, WatirPump::Constants::READABLES, WatirPump::Constants::WRITABLES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from WatirPump::Components::Flag

flag_accessor, flag_reader, flag_writer

Methods included from WatirPump::Components::DropdownList

select_accessor, select_reader, select_writer

Methods included from WatirPump::Components::CheckboxGroup

checkbox_accessor, checkbox_reader, checkbox_writer

Methods included from WatirPump::Components::RadioGroup

radio_accessor, radio_reader, radio_writer

Constructor Details

#initialize(browser, parent = nil, root_node = nil) ⇒ Component

Invoked implicity by WatirPump framework.

Parameters:

  • browser (Watir::Browser)

    Reference to browser instance

  • parent (Component) (defaults to: nil)

    Parent Component

  • root_node (Watir::Element) (defaults to: nil)

    Component mounting point in the DOM tree



357
358
359
360
361
# File 'lib/watir_pump/component.rb', line 357

def initialize(browser, parent = nil, root_node = nil)
  @browser = browser
  @parent = parent
  @root_node = root_node
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &blk) ⇒ Object (private)



493
494
495
496
497
498
499
500
# File 'lib/watir_pump/component.rb', line 493

def method_missing(name, *args, &blk)
  # delegate missing methods to current RSpec example if set
  example = WatirPump.config.current_example
  if example&.instance_exec { respond_to? name }
    return example.instance_exec { send(name, *args, &blk) }
  end
  super
end

Instance Attribute Details

#browserWatir::Browser (readonly)

Reference to browser instance

Returns:

  • (Watir::Browser)


56
57
58
# File 'lib/watir_pump/component.rb', line 56

def browser
  @browser
end

#parentComponent (readonly)

Parent WatirPump::Component. nil for Pages

Returns:



60
61
62
# File 'lib/watir_pump/component.rb', line 60

def parent
  @parent
end

Class Method Details

.component(name, klass, loc_method = nil, *loc_args) ⇒ Object

Declares a component of a given class under a given method name.

Examples:

component :login_form1, LoginForm, :div, id: 'login_form1'
component :login_form2, LoginForm, -> { root.div(id: 'login_form2') }

Parameters:

  • name (Symbol)

    Name of the method to access the component instance

  • klass (Class)

    Class of the declared component



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/watir_pump/component.rb', line 276

def component(name, klass, loc_method = nil, *loc_args)
  define_method(name) do |*args|
    node = find_element_raw(watir_method: loc_method,
                            watir_method_args: loc_args,
                            code: loc_method,
                            code_args: args)
    unless node.is_a?(Watir::Element) || node.nil?
      raise Errors::ElementMismatch.new(
        expected: Watir::Element,
        actual: node.class
      )
    end
    klass.new(browser, self, node)
  end
end

.components(name, klass, loc_method = nil, *loc_args) ⇒ Object

Declares a component collection

of a given class under a given method name.

Examples:

components :products1, ProductList, :divs, class: 'product'
components :products2, ProductList, -> { root.divs(class: 'product') }

Parameters:

  • name (Symbol)

    Name of the method to access the component list

  • klass (Class)

    Class of the component in the list



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/watir_pump/component.rb', line 300

def components(name, klass, loc_method = nil, *loc_args)
  define_method(name) do |*args|
    nodes = find_element_raw(watir_method: loc_method,
                             watir_method_args: loc_args,
                             code: loc_method,
                             code_args: args)
    unless nodes.is_a?(Watir::ElementCollection)
      raise Errors::ElementMismatch.new(
        expected: Watir::ElementCollection,
        actual: nodes.class
      )
    end
    ComponentCollection.new(nodes.map { |n| klass.new(browser, self, n) })
  end
end

.custom_reader(name, code = nil) ⇒ Object

Declares a custom element reader.

Examples:

custom_reader :price, -> { root.span(id: 'price').text.to_f }
custom_reader :price2

def price2
  root.span(id: 'price').text.to_f
end

Parameters:

  • name (Symbol)

    Name of the reader method

  • Method (Proc)

    body (optional). If not provided a regular instance with given name has to be declared



104
105
106
107
# File 'lib/watir_pump/component.rb', line 104

def custom_reader(name, code = nil)
  form_field_readers << name
  query(name, code) if code
end

.custom_writer(name, code = nil) ⇒ Object

Declares a custom element writer.

Examples:

custom_writer :price, ->(v) { root.text_field(id: 'price').set(v) }
custom_writer :price2

def price2=(v)
  root.text_field(id: 'price').set(v)
end

Parameters:

  • name (Symbol)

    Name of the writer method (without trailing ‘=’)

  • Method (Proc)

    body (optional). If not provided a regular instance with given name (with trailing ‘=’) has to be declared



121
122
123
124
# File 'lib/watir_pump/component.rb', line 121

def custom_writer(name, code = nil)
  form_field_writers << name
  query("#{name}=", code) if code
end

.decorate(method, *klasses) ⇒ Object

Decorate the result of given method with a list of wrapper classes.

Examples:

decorate :products, ProductCollection, AccessByNameCollection

Parameters:

  • method (Symbol)

    Name of the method to be decorated

  • klasses (*Class)

    List of wrapper classes



323
324
325
326
327
328
329
330
331
# File 'lib/watir_pump/component.rb', line 323

def decorate(method, *klasses)
  klasses.each do |klass|
    original_name = "#{method}_before_#{klass}".to_sym
    alias_method original_name, method
    define_method method do |*args|
      klass.new(send(original_name, *args))
    end
  end
end

.element(name, p) ⇒ Watir::Element

Declares an element located with lambda.

Examples:

element :name, -> { root.span(id: 'name') }

Returns:

  • (Watir::Element)


217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/watir_pump/component.rb', line 217

def element(name, p)
  define_method(name) do |*args|
    ret = instance_exec(*args, &p)
    unless ret.is_a?(Watir::Element)
      raise Errors::ElementMismatch.new(
        expected: Watir::Element,
        actual: ret.class
      )
    end
    ret
  end
end

.elements(name, p) ⇒ Watir::ElementCollection

Declares an element collection located with lambda.

Examples:

# mind the plural in watir method name: `lis` - not `li`!
elements :items, -> { root.lis(class: 'item') }

Returns:

  • (Watir::ElementCollection)


236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/watir_pump/component.rb', line 236

def elements(name, p)
  define_method(name) do |*args|
    ret = instance_exec(*args, &p)
    unless ret.is_a?(Watir::ElementCollection)
      raise Errors::ElementMismatch.new(
        expected: Watir::ElementCollection,
        actual: ret.class
      )
    end
    ret
  end
end

.form_field_readersSet<Symbol>

Returns a set of declared element readers. Used by #form_data

Returns:

  • (Set<Symbol>)


76
77
78
# File 'lib/watir_pump/component.rb', line 76

def form_field_readers
  @form_field_readers ||= Set.new
end

.form_field_writersSet<Symbol>

Returns a set of declared element writers. Used by #fill_form

Returns:

  • (Set<Symbol>)


83
84
85
# File 'lib/watir_pump/component.rb', line 83

def form_field_writers
  @form_field_writers ||= Set.new
end

.inspect_properties(properties) ⇒ Object

Define the customized ‘inspect` method that will return the desired properties Useful especially when working with testing frameworks that inspect the object under test in case of expectations not being met

Parameters:

  • properties (Symbol)

    (methods) to be included in the result

Returns:

  • Hash



341
342
343
344
345
346
347
348
349
# File 'lib/watir_pump/component.rb', line 341

def inspect_properties(properties)
  define_method :inspect do
    ret = {}
    properties.each do |p|
      ret[p] = public_send(p)
    end
    ret
  end
end

.query(name, p) ⇒ Object

A shorthand to generate one-liner instance methods

Examples:

query :sum, ->(a, b) { a + b }
# is equivalent to:
def sum(a, b)
  a + b
end

Parameters:

  • name (Symbol)

    Name of the method

  • p (Proc)

    Body of the method



206
207
208
209
210
# File 'lib/watir_pump/component.rb', line 206

def query(name, p)
  define_method(name) do |*args|
    instance_exec(*args, &p)
  end
end

.region(name, loc_method = nil, *loc_args, &blk) ⇒ Object

Declares anonymous component (namespace).

Examples:

class HomePage < WatirPump::Page
  region :login_box, :div, id: 'login_box' do
    text_field :username, id: 'user'
    text_field :password, id: 'pass'
    button :login, id: 'login'
  end

  def (user, pass)
    .username.set user
    .password.set pass
    ..click
  end
end


264
265
266
267
# File 'lib/watir_pump/component.rb', line 264

def region(name, loc_method = nil, *loc_args, &blk)
  klass = Class.new(Component) { instance_exec(&blk) }
  component(name, klass, loc_method, *loc_args)
end

Instance Method Details

#fill_form(data) ⇒ Object

Invokes element writer methods of given names with given values

Examples:

class MyPage < WatirPump::Page
  text_field_writer :first_name, id: 'first_name'
  text_field_writer :last_name, id: 'last_name'
  flag_writer :confirmed, id: 'confirmed'
end
MyPage.use do
  fill_form(first_name: 'John', last_name: 'Smith', confirmed: true)
  # is a shorthand for:
  self.first_name = 'John'
  self.last_name = 'Smith'
  self.confirmed = true
end

Parameters:

  • Hash

    data Names of writer methods (symbols), and values for them.



391
392
393
394
395
396
397
398
399
# File 'lib/watir_pump/component.rb', line 391

def fill_form(data)
  missing = data.to_h.keys - form_field_writers.to_a
  unless missing.empty?
    raise "#{self.class.name} does not contain writer(s) for #{missing}"
  end
  data.to_h.each_pair do |k, v|
    send("#{k}=", v)
  end
end

#fill_form!(data) ⇒ Object

Same as #fill_form but additionally invokes ‘submit` method if it exists. Otherwise raises an Exception.

Parameters:

  • Hash

    data Names of writer methods (symbols), and values for them.



405
406
407
408
409
# File 'lib/watir_pump/component.rb', line 405

def fill_form!(data)
  fill_form(data)
  raise ':fill_form! requries :submit method' unless respond_to? :submit
  submit
end

#form_dataHash

Invokes all reader methods at once and returns their values.

Examples:

class MyPage < WatirPump::Page
  span_reader :first_name, id: 'first_name'
  span_reader :last_name, id: 'last_name'
  flag_reader :confirmed, id: 'confirmed'
end
MyPage.use do
  data = form_data
  data == {first_name: 'John', last_name: 'Smith', confirmed: true}
end

Returns:

  • (Hash)

    Names of declared reader methods (symbols) and values they returned.



425
426
427
428
429
430
431
# File 'lib/watir_pump/component.rb', line 425

def form_data
  {}.tap do |h|
    form_field_readers.map do |field|
      h[field] = send(field)
    end
  end
end

#inspectObject

Customized Ruby’s object inspection method

Returns:

  • String



436
437
438
439
440
# File 'lib/watir_pump/component.rb', line 436

def inspect
  # rubocop:disable Metrics/LineLength
  "<#{self.class.name} parent: #{parent.class.name} methods: #{public_methods(false).join(', ')}>"
  # rubocop:enable Metrics/LineLength
end

#rootWatir::Element Also known as: node

Component mounting point in the DOM tree. All component elements are located relatively to it. For Page instances it points to browser

Returns:

  • (Watir::Element)


368
369
370
371
372
# File 'lib/watir_pump/component.rb', line 368

def root
  return @root_node if @root_node
  return browser if parent.nil?
  parent.root
end