module AngularWebdriver

Constants

DATE
VERSION

Public Class Methods

define_page_methods(opts={}) click to toggle source
# File lib/angular_webdriver/protractor/rspec_helpers.rb, line 28
def define_page_methods opts={}
  method        = opts.fetch(:method, :define_singleton_method)
  page_module   = opts[:page_module] || raise('must set page_module')
  target_class  = opts[:target_class] || raise('must set target_class')
  driver_object = opts[:watir] || opts[:driver] || raise('must set driver')
  page_module.constants.each do |page_class|
    qualified_class = page_module.const_get(page_class)

    # enable use of by/element/no_wait within block passed to pageobject element
    # element(:greet_button) { element(by.binding('greet'))   }
    AngularWebdriver.install_rspec_helpers qualified_class

    # ButtonsPage => buttons_page
    # https://github.com/rails/rails/blob/daaa21bc7d20f2e4ff451637423a25ff2d5e75c7/activesupport/lib/active_support/inflector/methods.rb#L96
    page_name = page_class.to_s.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
    target_class.send(method, page_name) do
      instance_name = "@#{page_module}#{page_class}"
      # must always set because the driver may have changed sessions
      obj = qualified_class.new driver_object
      instance_variable_set instance_name, obj
    end
  end
end
install_rspec_helpers(context = RSpec::Core::ExampleGroup) click to toggle source

call after invoking Protractor.new. Installs by/element/no_wait

# File lib/angular_webdriver/protractor/rspec_helpers.rb, line 10
def install_rspec_helpers context = RSpec::Core::ExampleGroup
  helpers = AngularWebdriver::RSpecHelpers
  helpers.singleton_methods(false).each do |method_symbol|
    context.send(:define_method, method_symbol) do |*args, &block|
      args.length == 0 ? helpers.send(method_symbol, &block) :
        helpers.send(method_symbol, *args, &block)
    end
  end
end
patch_watir() click to toggle source

Patch watir must be invoked before watir-webdriver is required to ensure the methods are defined before inheritance occurs. Then patch_watir must be invoked after requiring watir-webdriver to restore the methods that were overridden.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 14
def self.patch_watir
  #
  # This patch serves a few purposes. The first is matching Protractor semantics
  # of lazy finding elements and always relocating elements (ex: element.text)
  #
  # The second is removing unnecessary bloatware from Watir which has a number
  # of checks that don't make sense for angular.js testing. The specifics
  # of this patch will change in the next Watir release. Currently version
  # 0.7.0 is targeted.
  #
  # The third is teaching Watir about angular specific locators
  #
  # Design goal: element.all(by.binding('slowHttpStatus'))
  #              should not make any server requests
  #

  ::Watir::Browser.class_eval do
    # avoid prepending 'http://' to non-urls because protractor handles
    # the prepending at the driver level.
    def goto(uri)
      @driver.navigate.to uri
      run_checkers

      url
    end
  end

  ::Watir::HTMLElementCollection.class_eval do
    # Return original selector.
    # Method added for protractor compatibility
    def locator
      @selector
    end
  end

  ::Watir::Container.module_eval do
    #
    # Alias of elements for Protractor
    #

    def all(*args)
      elements(*args)
    end

    # Redefine extract_selector to wrap find by repeater
    upstream_extract_selector = instance_method(:extract_selector)
    define_method(:extract_selector) do |selectors|
      selectors = AngularWebdriver::ByRepeaterInner.wrap_repeater selectors

      upstream_extract_selector.bind(self).call selectors
    end

  end # ::Watir::Container.module_eval

  #
  # Base class for HTML elements.
  #

  # Note the element class is different on master.

  ::Watir::Element.class_eval do
    # Always raise on stale element ref error. Prevents infinite retry loop.
    def element_call
      yield
    rescue Selenium::WebDriver::Error::StaleElementReferenceError
      raise
    end

    # Rescue all exceptions. Guarantee that we'll return true or false.
    #
    # Returns true if element exists and false otherwise.
    def exists?
      assert_exists
      true
    rescue Exception
      false
    end

    def selected?
      assert_exists
      element_call { @element.selected? }
    end

    # required for watir otherwise execute_script will fail
    #
    # e = browser.element(tag_name: 'div')
    # driver.execute_script 'return arguments[0].tagName', e
    # {"script":"return arguments[0].tagName","args":[{"ELEMENT":"0"}]}
    #
    # Convert to a WebElement JSON Object for transmission over the wire.
    # @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#basic-terms-and-concepts
    #
    # @api private
    #
    def to_json(*args)
      assert_exists
      { ELEMENT: @element.ref }.to_json
    end

    # Ensure that the element exists by always relocating it
    # Required to trigger waitForAngular. Caching the element here will
    # break the Protractor sync feature so this must be @element = locate.
    def assert_exists
      @element = locate
    end

    def assert_not_stale
      nil
    end

    def assert_enabled
      nil
    end

    # Return original selector.
    # Method added for protractor compatibility
    def locator
      @selector
    end

    # avoid context lookup
    def locate
      locator_class.new(@parent.wd, @selector, self.class.attribute_list).locate
    end

    # Invoke protractor.allowAnimations with freshly located element and
    # optional value.
    def allowAnimations value=nil
      assert_exists
      driver.protractor.allowAnimations @element, value
    end

    # Watir doesn't define a clear method on element so we have to provide one.
    def clear
      assert_exists
      element_call { @element.clear }
    end

    # Evaluate an Angular expression as if it were on the scope
    # of the current element.
    #
    # @param expression <String> The expression to evaluate.
    #
    # @return <Object> The result of the evaluation.
    def evaluate expression
      assert_exists
      driver.protractor.evaluate @element, expression
    end

    #
    # Returns true if the element exists and is visible on the page.
    # Returns false if the element doesn't exist or isn't visible.
    #
    # @return [Boolean]
    # @see Watir::Wait
    #
    #
    # rescue element not found
    def present?
      exists? && visible?
    rescue Exception
      # if the element disappears between the exists? and visible? calls,
      # consider it not present.
      false
    end
  end # ::Watir::Element.class_eval

  #
  # The main class through which you control the browser.
  #

  ::Watir::Browser.class_eval do
    def assert_exists
      # remove expensive window check
      raise Exception::Error, 'browser was closed' if @closed
    end

    def inspect
      nil # avoid expensive browser url and title lookup
    end
  end # ::Watir::Browser.class_eval

  ::Watir::ElementLocator.class_eval do
    def validate_element(element)
      tn = @selector[:tag_name]
      return element unless tn # don't validate nil tag names
      element_tag_name = element.tag_name.downcase

      return if tn && !tag_name_matches?(element_tag_name, tn)

      if element_tag_name == 'input'
        return if @selector[:type] && @selector[:type] != element.attribute(:type)
      end

      element
    end

    # always raise element not found / stale reference error
    def locate
      # element.all(by.partialButtonText('text')).to_a[0].value creates the
      # selector {:element=>#<Selenium::WebDriver::Element ...>}
      # in that case we've already located the element.
      #
      # see 'should find multiple buttons containing "text"' in locators_spec.rb
      return @selector[:element] if @selector.is_a?(Hash) && @selector[:element].is_a?(Selenium::WebDriver::Element)

      e = by_id and return e # short-circuit if :id is given

      if @selector.size == 1
        element = find_first_by_one
      else
        element = find_first_by_multiple
      end

      # This actually only applies when finding by xpath/css - browser.text_field(:xpath, "//input[@type='radio']")
      # We don't need to validate the element if we built the xpath ourselves.
      # It is also used to alter behavior of methods locating more than one type of element
      # (e.g. text_field locates both input and textarea)
      validate_element(element) if element
    end
  end # ::Watir::ElementLocator.class_eval
end
require_all_pages(glob_path=nil) click to toggle source
# File lib/angular_webdriver/protractor/rspec_helpers.rb, line 20
def require_all_pages glob_path=nil
  base_dir = Rake.application.original_dir if defined?(Rake)
  base_dir ||= @original_dir ||= Dir.pwd

  glob_path ||= File.join(base_dir, 'page', '**', '*.rb')
  Dir.glob(glob_path) { |file| require_relative file }
end

Public Instance Methods

all(*args) click to toggle source

Alias of elements for Protractor

# File lib/angular_webdriver/protractor/watir_patch.rb, line 54
def all(*args)
  elements(*args)
end
allowAnimations(value=nil) click to toggle source

Invoke protractor.allowAnimations with freshly located element and optional value.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 141
def allowAnimations value=nil
  assert_exists
  driver.protractor.allowAnimations @element, value
end
assert_enabled() click to toggle source
# File lib/angular_webdriver/protractor/watir_patch.rb, line 124
def assert_enabled
  nil
end
assert_exists() click to toggle source

Ensure that the element exists by always relocating it Required to trigger waitForAngular. Caching the element here will break the Protractor sync feature so this must be @element = locate.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 116
def assert_exists
  @element = locate
end
assert_not_stale() click to toggle source
# File lib/angular_webdriver/protractor/watir_patch.rb, line 120
def assert_not_stale
  nil
end
clear() click to toggle source

Watir doesn’t define a clear method on element so we have to provide one.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 147
def clear
  assert_exists
  element_call { @element.clear }
end
element_call() { || ... } click to toggle source

Always raise on stale element ref error. Prevents infinite retry loop.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 76
def element_call
  yield
rescue Selenium::WebDriver::Error::StaleElementReferenceError
  raise
end
evaluate(expression) click to toggle source

Evaluate an Angular expression as if it were on the scope of the current element.

@param expression <String> The expression to evaluate.

@return <Object> The result of the evaluation.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 158
def evaluate expression
  assert_exists
  driver.protractor.evaluate @element, expression
end
exists?() click to toggle source

Rescue all exceptions. Guarantee that we’ll return true or false.

Returns true if element exists and false otherwise.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 85
def exists?
  assert_exists
  true
rescue Exception
  false
end
goto(uri) click to toggle source

avoid prepending ‘http://’ to non-urls because protractor handles the prepending at the driver level.

# File lib/angular_webdriver/protractor/watir_patch.rb, line 33
def goto(uri)
  @driver.navigate.to uri
  run_checkers

  url
end
inspect() click to toggle source
# File lib/angular_webdriver/protractor/watir_patch.rb, line 191
def inspect
  nil # avoid expensive browser url and title lookup
end
locate() click to toggle source

avoid context lookup

# File lib/angular_webdriver/protractor/watir_patch.rb, line 135
def locate
  locator_class.new(@parent.wd, @selector, self.class.attribute_list).locate
end
locator() click to toggle source

Return original selector. Method added for protractor compatibility

# File lib/angular_webdriver/protractor/watir_patch.rb, line 44
def locator
  @selector
end
present?() click to toggle source

Returns true if the element exists and is visible on the page. Returns false if the element doesn’t exist or isn’t visible.

@return [Boolean] @see Watir::Wait

rescue element not found

# File lib/angular_webdriver/protractor/watir_patch.rb, line 172
def present?
  exists? && visible?
rescue Exception
  # if the element disappears between the exists? and visible? calls,
  # consider it not present.
  false
end
selected?() click to toggle source
# File lib/angular_webdriver/protractor/watir_patch.rb, line 92
def selected?
  assert_exists
  element_call { @element.selected? }
end
to_json(*args) click to toggle source

required for watir otherwise execute_script will fail

e = browser.element(tag_name: ‘div’) driver.execute_script ‘return arguments.tagName’, e {“script”:“return arguments.tagName”,“args”:}

Convert to a WebElement JSON Object for transmission over the wire. @see github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#basic-terms-and-concepts

@api private

# File lib/angular_webdriver/protractor/watir_patch.rb, line 108
def to_json(*args)
  assert_exists
  { ELEMENT: @element.ref }.to_json
end
validate_element(element) click to toggle source
# File lib/angular_webdriver/protractor/watir_patch.rb, line 197
def validate_element(element)
  tn = @selector[:tag_name]
  return element unless tn # don't validate nil tag names
  element_tag_name = element.tag_name.downcase

  return if tn && !tag_name_matches?(element_tag_name, tn)

  if element_tag_name == 'input'
    return if @selector[:type] && @selector[:type] != element.attribute(:type)
  end

  element
end