class CapybaraTestHelpers::TestHelper

Public: Base class to create test helpers that have full access to the Capybara DSL, while easily defining custom assertions, getters, and actions.

It also supports locator aliases to prevent duplication and keep tests easier to understand and to maintain.

Attributes

query_context[R]
test_context[R]

Public Class Methods

new(query_context, test_context: query_context, negated: nil) click to toggle source
# File lib/capybara_test_helpers/test_helper.rb, line 36
def initialize(query_context, test_context: query_context, negated: nil)
  @query_context, @test_context, @negated = query_context, test_context, negated
end

Private Class Methods

delegate_to_test_context(*method_names) click to toggle source

Public: Make methods in the test context available in the helpers.

# File lib/capybara_test_helpers/test_helper.rb, line 137
def delegate_to_test_context(*method_names)
  delegate(*method_names, to: :test_context)
end
method_added(method_name) click to toggle source

Internal: Fail early if a reserved method is redefined.

# File lib/capybara_test_helpers/test_helper.rb, line 167
def method_added(method_name)
  return unless CapybaraTestHelpers::RESERVED_METHODS.include?(method_name)

  raise "A method with the name #{ method_name.inspect } is part of the Capybara DSL," \
    ' overriding it could cause unexpected issues that could be very hard to debug.'
end
on_test_helper_load() click to toggle source

Internal: Allows to perform certain actions just before a test helper will be loaded for the first time.

# File lib/capybara_test_helpers/test_helper.rb, line 162
def on_test_helper_load
  define_getters_for_selectors
end
use_test_helpers(*helper_names) click to toggle source

Public: Allows to make other test helpers available.

NOTE: When you call a helper the “negated” state is preserved for assertions.

NOTE: You can also pass an element to a test helper to “wrap” a specified element with the specified test helper class.

Example:

dropdown(element).toggle_menu
# File lib/capybara_test_helpers/test_helper.rb, line 150
def use_test_helpers(*helper_names)
  helper_names.each do |helper_name|
    private define_method(helper_name) { |element = nil|
      test_helper = test_context.get_test_helper(helper_name)
      test_helper = test_helper.wrap_element(element) if element
      @negated.nil? ? test_helper : test_helper.should(@negated)
    }
  end
end

Public Instance Methods

friendly_name() click to toggle source

Internal: Returns the name of the class without the suffix.

Example: 'current_page' for CurrentPageTestHelper.

# File lib/capybara_test_helpers/test_helper.rb, line 102
def friendly_name
  self.class.name.chomp('TestHelper').underscore
end
inspect() click to toggle source

Public: To make the benchmark log less verbose.

# File lib/capybara_test_helpers/test_helper.rb, line 41
def inspect
  %(#<#{ self.class.name } #{ current_element? ? %(tag="#{ base.tag_name }") : object_id }>)
rescue *page.driver.invalid_element_errors
  %(#<#{ self.class.name } #{ object_id }>)
end
inspect_node() click to toggle source

Public: Makes it easier to inspect the current element.

# File lib/capybara_test_helpers/test_helper.rb, line 48
def inspect_node
  to_capybara_node.inspect
end
to_capybara_node() click to toggle source

Public: Casts the current context as a Capybara::Node::Element.

NOTE: Uses the :el convention, which means actions can be performed directly on the test helper if an :el selector is defined.

# File lib/capybara_test_helpers/test_helper.rb, line 56
def to_capybara_node
  return current_context if current_element?
  return find_element(:el) if selectors.key?(:el)

  raise_missing_element_error
end
within(*args, **kwargs, &block) click to toggle source

Public: Scopes the Capybara queries in the block to be inside the specified selector.

# File lib/capybara_test_helpers/test_helper.rb, line 85
def within(*args, **kwargs, &block)
  return be_within(*args, **kwargs) unless block_given? # RSpec matcher.

  within_element(*args, **kwargs, &block)
end
within_document() { |wrap_element(document)| ... } click to toggle source

Public: Unscopes the inner block from any previous `within` calls.

# File lib/capybara_test_helpers/test_helper.rb, line 92
def within_document
  page.instance_exec { scopes << nil }
  yield wrap_element(page.document)
ensure
  page.instance_exec { scopes.pop }
end
within_element(*args, **kwargs, &block) click to toggle source

Public: Scopes the Capybara queries in the block to be inside the specified selector.

# File lib/capybara_test_helpers/test_helper.rb, line 77
def within_element(*args, **kwargs, &block)
  locator = args.empty? ? [self] : args
  kwargs[:test_helper] = self
  page.within(*locator, **kwargs, &block)
end
wrap_element(element) click to toggle source

Public: Wraps a Capybara::Node::Element or test helper with a test helper object of this class.

# File lib/capybara_test_helpers/test_helper.rb, line 65
def wrap_element(element)
  if element.is_a?(Enumerable)
    element.map { |node| wrap_element(node) }
  else
    raise ArgumentError, "#{ element.inspect } must be a test helper or element." unless element.respond_to?(:to_capybara_node)

    self.class.new(element.to_capybara_node, test_context: test_context)
  end
end

Protected Instance Methods

current_context() click to toggle source

Internal: Used to perform assertions and others.

# File lib/capybara_test_helpers/test_helper.rb, line 109
def current_context
  query_context.respond_to?(:to_capybara_node) ? query_context : page
end
current_element?() click to toggle source

Internal: Returns true if the current context is an element.

# File lib/capybara_test_helpers/test_helper.rb, line 114
def current_element?
  current_context.is_a?(Capybara::Node::Element)
end

Private Instance Methods

raise_missing_element_error() click to toggle source

Internal: Helper to provide more information on the error.

# File lib/capybara_test_helpers/test_helper.rb, line 127
def raise_missing_element_error
  method_caller = caller.select { |x| x['test_helpers'] }[1]
  method_caller_name = method_caller&.match(/in `(\w+)'/)
  method_caller_name = method_caller_name ? method_caller_name[1] : method_caller
  raise ArgumentError, "You are calling the `#{ method_caller_name }' method on the test helper but :el is not defined nor there's a current element.\n"\
  'Define :el, or find an element before performing the action.'
end
wrap_filter(filter) click to toggle source

Internal: Wraps the optional filter block to ensure we pass it a test helper instead of a raw Capybara::Node::Element.

# File lib/capybara_test_helpers/test_helper.rb, line 122
def wrap_filter(filter)
  proc { |capybara_node_element| filter.call(wrap_element(capybara_node_element)) } if filter
end