Module: Testable

Defined in:
lib/testable/errors.rb,
lib/testable.rb,
lib/testable/page.rb,
lib/testable/ready.rb,
lib/testable/logger.rb,
lib/testable/region.rb,
lib/testable/context.rb,
lib/testable/element.rb,
lib/testable/locator.rb,
lib/testable/version.rb,
lib/testable/attribute.rb,
lib/testable/situation.rb,
lib/testable/deprecator.rb,
lib/testable/extensions/data_setter.rb

Overview

rubocop:disable Style/DocumentationMethod

Defined Under Namespace

Modules: Context, DataSetter, Element, Errors, Pages, Ready, Situation Classes: Deprecator, Logger

Constant Summary collapse

VERSION =
"1.0.0".freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.browserObject

This accessor is needed so that Testable itself can provide a browser reference to indicate connection to WebDriver. This is a class-level access to the browser.



211
212
213
# File 'lib/testable.rb', line 211

def browser
  @browser
end

Instance Attribute Details

#browserObject

This accessor is needed so that internal API calls, like ‘markup` or `text`, have access to the browser instance. This is also necessary in order for element handling to be called appropriately on the a valid browser instance. This is an instance-level access to whatever browser Testable is using.



82
83
84
# File 'lib/testable.rb', line 82

def browser
  @browser
end

#parentObject (readonly)

This accessor is needed so that the parent of a region is recognized as part of the overall execution context of Testable.



90
91
92
# File 'lib/testable.rb', line 90

def parent
  @parent
end

#region_elementObject (readonly)

This accessor is needed so that the region_element is recognized as part of the overall execution context of Testable.



86
87
88
# File 'lib/testable.rb', line 86

def region_element
  @region_element
end

Class Method Details

.apiObject

This provides the API that is defined by Testable when you have a working model, such as a page object.



232
233
234
# File 'lib/testable.rb', line 232

def api
  methods - Object.public_methods
end

.configure {|_self| ... } ⇒ Object

Provides a means to allow a configure block on Testable. This allows you to setup Testable, as such:

Testable.configure do |config|
  config.driver_timeout = 5
  config.wire_level_logging = :info
  config.log_level = :debug
end

Yields:

  • (_self)

Yield Parameters:

  • _self (Testable)

    the object that the method was called on



101
102
103
# File 'lib/testable.rb', line 101

def configure
  yield self
end

.dependenciesObject

Returns all of the dependencies that Testable relies on.



25
26
27
28
# File 'lib/testable/version.rb', line 25

def dependencies
  Gem.loaded_specs.values.map { |spec| "#{spec.name} #{spec.version}\n" }
     .uniq.sort.join(",").split(",")
end

.driver_timeout=(value) ⇒ Object

Watir provides a default timeout of 30 seconds. This allows you to change that in the Testable context. For example:

Testable.driver_timeout = 5

This would equivalent to doing this:

Watir.default_timeout = 5


113
114
115
# File 'lib/testable.rb', line 113

def driver_timeout=(value)
  Watir.default_timeout = value
end

.elementsObject

Provides a list of all elements that Watir knows how to work with. This is generated from the Watir container itself so the list should always be up to date.



24
25
26
# File 'lib/testable/element.rb', line 24

def elements
  @elements ||= Watir::Container.instance_methods unless @elements
end

.elements?Boolean

This predicate method returns all of the elements that can be recognized by Watir.

Returns:

  • (Boolean)


11
12
13
# File 'lib/testable/element.rb', line 11

def elements?
  @elements
end

.gem_version(name) ⇒ Object

Returns a gem version for a given gem, assuming the gem has been loaded.



18
19
20
21
22
# File 'lib/testable/version.rb', line 18

def gem_version(name)
  Gem.loaded_specs[name].version
rescue NoMethodError
  puts "No gem loaded for #{name}."
end

.included(caller) ⇒ Object

This is the core method of Testable which makes the various components of the framework available to tests.



21
22
23
24
25
26
27
28
29
# File 'lib/testable.rb', line 21

def self.included(caller)
  caller.extend Testable::Pages::Attribute
  caller.extend Testable::Pages::Element
  caller.extend Testable::Pages::Region
  caller.__send__ :include, Testable::Ready
  caller.__send__ :include, Testable::Pages
  caller.__send__ :include, Testable::Element::Locator
  caller.__send__ :include, Testable::DataSetter
end

.log_levelObject

To query what level is being logged, do this:

Testable.log_level

The logging level will be UNKNOWN by default.



149
150
151
# File 'lib/testable.rb', line 149

def log_level
  %i[DEBUG INFO WARN ERROR FATAL UNKNOWN][logger.level]
end

.log_level=(value) ⇒ Object

To enable logging, do this:

Testable.log_level = :DEBUG
Testable.log_level = 'DEBUG'
Testable.log_level = 0

This can accept any of a Symbol / String / Integer as an input To disable all logging, which is the case by default, do this:

Testable.log_level = :UNKNOWN


140
141
142
# File 'lib/testable.rb', line 140

def log_level=(value)
  logger.level = value
end

.log_path=(logdev) ⇒ Object

The writer method allows you to configure where you want the output of the Testable logs to go, with the default being standard output. Here is how you could change this to a specific file:

Testable.log_path = 'testable.log'


158
159
160
# File 'lib/testable.rb', line 158

def log_path=(logdev)
  logger.reopen(logdev)
end

.loggerObject

The Testable logger object. To log messages:

Testable.logger.info('Some information.')
Testable.logger.debug('Some diagnostics')

To alter or check the current logging level, you can call ‘.log_level=` or `.log_level`. By default the logger will output all messages to the standard output ($stdout) but it can be altered to log to a file or to another IO location by calling `.log_path=`.



126
127
128
# File 'lib/testable.rb', line 126

def logger
  @logger ||= Testable::Logger.new.create
end

.plural?(method) ⇒ Boolean

This predicate method checks if a given element is a valid element that Watir knows how to interact with, but in plural form.

Examples:

Testable.recognizes?(:spans) # => true
Testable.recognizes?(:divs) # => true
Testable.recognizes?(:class) # => false
Testable.recognizes?(:classes) # => false

Returns:

  • (Boolean)


36
37
38
39
40
41
42
43
44
45
46
# File 'lib/testable/element.rb', line 36

def plural?(method)
  value = method.to_s
  plural = value.to_sym

  # Remove the trailing "s" or "es" to de-pluralize.
  single = value.sub(/e?s$/, '').to_sym

  !value.match(/s$/).nil? &&
    @elements.include?(plural) &&
    @elements.include?(single)
end

.pluralize(method) ⇒ Object

This method will allow a particular element name to be pluralized.

Examples:

Empirical.pluralize(:div) => divs
Empirical.pluralize(:checkbox) => checkboxes


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/testable/element.rb', line 53

def self.pluralize(method)
  value = method.to_s

  # This bit is not what you would call optimized. Essentially, it just
  # tries to ppluralize with "s" and see if that method is included in the
  # Watir methods. If that fails, then an attempt to pluralize with "es" is
  # tried. If that fails, the assumption is made that a pluralized form of
  # the Watir method does not exist.
  if @elements.include?(:"#{value}s")
    :"#{value}s"
  elsif @elements.include?(:"#{value}es")
    :"#{value}es"
  else
    raise Testable::Errors::PluralizedElementError, "Unable to find plural form for #{value}!"
  end
end

.quit_browserObject

Generates a quit event on the browser instance. This is passed through to Watir which will ultimately delegate browser handling to Selenium and thus WebDriver.



226
227
228
# File 'lib/testable.rb', line 226

def quit_browser
  @browser.quit
end

.recognizes?(method) ⇒ Boolean

This predicate method checks if a given element is in the list of known elements that Watir knows how to interact with.

Returns:

  • (Boolean)


17
18
19
# File 'lib/testable/element.rb', line 17

def recognizes?(method)
  @elements.include? method.to_sym
end

.selenium_apiObject

Returns information about the API that is specific to Selenium.



204
205
206
# File 'lib/testable.rb', line 204

def selenium_api
  browser.driver.methods - Object.public_methods
end

.set_browser(app = :chrome, *args) ⇒ Object Also known as: start_browser

This sets the browser instance so that Testable is aware of what that instance is. This is how a test, using the Testable framework, will be able to know which browser to communicate with.



216
217
218
219
# File 'lib/testable.rb', line 216

def set_browser(app = :chrome, *args)
  @browser = Watir::Browser.new(app, *args)
  Testable.browser = @browser
end

.versionObject

Returns version information about Testable and its core dependencies.



8
9
10
11
12
13
14
# File 'lib/testable/version.rb', line 8

def version
  """
Testable v#{Testable::VERSION}
watir: #{gem_version('watir')}
selenium-webdriver: #{gem_version('selenium-webdriver')}
  """
end

.watir_apiObject

Returns information about the API that Watir exposes. This does not include Selenium elements, which Watir is wrapping.



198
199
200
201
# File 'lib/testable.rb', line 198

def watir_api
  browser.methods - Object.public_methods -
    Watir::Container.instance_methods
end

.wire_level_loggingObject

Report the current logging level.



192
193
194
# File 'lib/testable.rb', line 192

def wire_level_logging
  %i[DEBUG INFO WARN ERROR FATAL UNKNOWN][Watir.logger.level]
end

.wire_level_logging=(value) ⇒ Object

Level of logging.



187
188
189
# File 'lib/testable.rb', line 187

def wire_level_logging=(value)
  Watir.logger.level = value
end

.wire_path=(logdev) ⇒ Object

Path for the logger to use.



182
183
184
# File 'lib/testable.rb', line 182

def wire_path=(logdev)
  Watir.logger.reopen(logdev)
end

Instance Method Details

#definition_setupObject

This method is used to setup any definitions provided as part of the executable of Testable. This generally means any page definitions that include Testable.



46
47
48
49
50
51
# File 'lib/testable.rb', line 46

def definition_setup
  begin_with if respond_to?(:begin_with)
  initialize_page if respond_to?(:initialize_page)
  initialize_region if respond_to?(:initialize_region)
  initialize_regions
end

#initialize(browser = nil, region_element = nil, parent = nil, &block) ⇒ Object

Initialization is used to establish a browser instance within the context of the framework. The logic here requires determining under what condition a browser instance may have been set.



34
35
36
37
38
39
40
41
# File 'lib/testable.rb', line 34

def initialize(browser = nil, region_element = nil, parent = nil, &block)
  @browser = Testable.browser unless Testable.browser.nil?
  @browser = browser if Testable.browser.nil?
  @region_element = region_element || Testable.browser
  @parent = parent
  definition_setup
  instance_eval(&block) if block
end

#initialize_regionsObject

This method is used to interate through any definitions that provide an “initialize_region” method. What happens here is essentially that new polymorphic modules are created based on classes. This is what allows a region to be part of a page definition, the latter of which is nothing more than a standard Ruby class.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/testable.rb', line 58

def initialize_regions
  @initialized_regions ||= []

  # The goal here is get all included and extended modules.
  modules = self.class.included_modules + (class << self; self end).included_modules
  modules.uniq!

  # Then each of those modules can be initialized.
  modules.each do |m|
    # First a check is made the that constructor is defined and that it has
    # not been called before.
    next if @initialized_regions.include?(m) || !m.instance_methods.include?(:initialize_region)

    m.instance_method(:initialize_region).bind(self).call

    @initialized_regions << m
  end
end