capybara_test_helpers 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/CHANGELOG.md +3 -0
 - data/README.md +401 -0
 - data/lib/capybara_test_helpers.rb +6 -0
 - data/lib/capybara_test_helpers/actions.rb +116 -0
 - data/lib/capybara_test_helpers/assertions.rb +124 -0
 - data/lib/capybara_test_helpers/benchmark_helpers.rb +94 -0
 - data/lib/capybara_test_helpers/config.rb +56 -0
 - data/lib/capybara_test_helpers/cucumber.rb +12 -0
 - data/lib/capybara_test_helpers/dependency_injection.rb +44 -0
 - data/lib/capybara_test_helpers/finders.rb +40 -0
 - data/lib/capybara_test_helpers/matchers.rb +55 -0
 - data/lib/capybara_test_helpers/rspec.rb +33 -0
 - data/lib/capybara_test_helpers/selectors.rb +174 -0
 - data/lib/capybara_test_helpers/synchronization.rb +41 -0
 - data/lib/capybara_test_helpers/test_helper.rb +182 -0
 - data/lib/capybara_test_helpers/to_or_expectation_handler.rb +27 -0
 - data/lib/capybara_test_helpers/version.rb +6 -0
 - data/lib/generators/test_helper/test_helper_generator.rb +26 -0
 - metadata +92 -0
 
| 
         @@ -0,0 +1,124 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'capybara_test_helpers/to_or_expectation_handler'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Internal: Wraps RSpec matchers to allow using them after calling `should` or
         
     | 
| 
      
 6 
     | 
    
         
            +
            # `should_not` to perform the assertion.
         
     | 
| 
      
 7 
     | 
    
         
            +
            module CapybaraTestHelpers::Assertions
         
     | 
| 
      
 8 
     | 
    
         
            +
              # Public: Allows writing custom on-demand matchers, as well as chaining
         
     | 
| 
      
 9 
     | 
    
         
            +
              # several assertions.
         
     | 
| 
      
 10 
     | 
    
         
            +
              def should(negated = false)
         
     | 
| 
      
 11 
     | 
    
         
            +
                negated = !!negated # Coerce to boolean.
         
     | 
| 
      
 12 
     | 
    
         
            +
                return self if negated == @negated
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                clone.tap { |test_helper| test_helper.instance_variable_set('@negated', negated) }
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
              [:should_still, :should_now, :and, :and_instead, :and_also, :and_still].each { |should_alias| alias_method should_alias, :should }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # Public: Allows writing custom on-demand matchers, as well as chaining
         
     | 
| 
      
 19 
     | 
    
         
            +
              # several assertions.
         
     | 
| 
      
 20 
     | 
    
         
            +
              def should_not
         
     | 
| 
      
 21 
     | 
    
         
            +
                @negated ? self : should(true)
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
              [:should_still_not, :should_no_longer, :nor, :and_not].each { |should_alias| alias_method should_alias, :should_not }
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              # Public: Makes it more readable when in used in combination with to_or.
         
     | 
| 
      
 26 
     | 
    
         
            +
              def not_to
         
     | 
| 
      
 27 
     | 
    
         
            +
                raise(ArgumentError, 'You must call `should` or `should_not` before calling this method') if @negated.nil?
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                @negated
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
              alias or_should_not not_to
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              # Public: Allows to write complex nested assertions.
         
     | 
| 
      
 34 
     | 
    
         
            +
              def invert_expectation
         
     | 
| 
      
 35 
     | 
    
         
            +
                should(!not_to)
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              %i[
         
     | 
| 
      
 39 
     | 
    
         
            +
                have_selector
         
     | 
| 
      
 40 
     | 
    
         
            +
                have_no_selector
         
     | 
| 
      
 41 
     | 
    
         
            +
                have_css
         
     | 
| 
      
 42 
     | 
    
         
            +
                have_no_css
         
     | 
| 
      
 43 
     | 
    
         
            +
                have_xpath
         
     | 
| 
      
 44 
     | 
    
         
            +
                have_no_xpath
         
     | 
| 
      
 45 
     | 
    
         
            +
                have_link
         
     | 
| 
      
 46 
     | 
    
         
            +
                have_no_link
         
     | 
| 
      
 47 
     | 
    
         
            +
                have_button
         
     | 
| 
      
 48 
     | 
    
         
            +
                have_no_button
         
     | 
| 
      
 49 
     | 
    
         
            +
                have_field
         
     | 
| 
      
 50 
     | 
    
         
            +
                have_no_field
         
     | 
| 
      
 51 
     | 
    
         
            +
                have_select
         
     | 
| 
      
 52 
     | 
    
         
            +
                have_no_select
         
     | 
| 
      
 53 
     | 
    
         
            +
                have_table
         
     | 
| 
      
 54 
     | 
    
         
            +
                have_no_table
         
     | 
| 
      
 55 
     | 
    
         
            +
                have_checked_field
         
     | 
| 
      
 56 
     | 
    
         
            +
                have_no_checked_field
         
     | 
| 
      
 57 
     | 
    
         
            +
                have_unchecked_field
         
     | 
| 
      
 58 
     | 
    
         
            +
                have_no_unchecked_field
         
     | 
| 
      
 59 
     | 
    
         
            +
                have_all_of_selectors
         
     | 
| 
      
 60 
     | 
    
         
            +
                have_none_of_selectors
         
     | 
| 
      
 61 
     | 
    
         
            +
                have_any_of_selectors
         
     | 
| 
      
 62 
     | 
    
         
            +
                have_title
         
     | 
| 
      
 63 
     | 
    
         
            +
                have_no_title
         
     | 
| 
      
 64 
     | 
    
         
            +
              ].each do |method_name|
         
     | 
| 
      
 65 
     | 
    
         
            +
                CapybaraTestHelpers.define_helper_method(self, method_name, assertion: true)
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              %i[
         
     | 
| 
      
 69 
     | 
    
         
            +
                have_ancestor
         
     | 
| 
      
 70 
     | 
    
         
            +
                have_no_ancestor
         
     | 
| 
      
 71 
     | 
    
         
            +
                have_sibling
         
     | 
| 
      
 72 
     | 
    
         
            +
                have_no_sibling
         
     | 
| 
      
 73 
     | 
    
         
            +
                match_selector
         
     | 
| 
      
 74 
     | 
    
         
            +
                not_match_selector
         
     | 
| 
      
 75 
     | 
    
         
            +
                match_css
         
     | 
| 
      
 76 
     | 
    
         
            +
                not_match_css
         
     | 
| 
      
 77 
     | 
    
         
            +
                match_xpath
         
     | 
| 
      
 78 
     | 
    
         
            +
                not_match_xpath
         
     | 
| 
      
 79 
     | 
    
         
            +
                have_text
         
     | 
| 
      
 80 
     | 
    
         
            +
                have_no_text
         
     | 
| 
      
 81 
     | 
    
         
            +
                have_content
         
     | 
| 
      
 82 
     | 
    
         
            +
                have_no_content
         
     | 
| 
      
 83 
     | 
    
         
            +
                match_style
         
     | 
| 
      
 84 
     | 
    
         
            +
                have_style
         
     | 
| 
      
 85 
     | 
    
         
            +
              ].each do |method_name|
         
     | 
| 
      
 86 
     | 
    
         
            +
                CapybaraTestHelpers.define_helper_method(self, method_name, target: :to_capybara_node, assertion: true)
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
              %i[
         
     | 
| 
      
 90 
     | 
    
         
            +
                have_current_path
         
     | 
| 
      
 91 
     | 
    
         
            +
                have_no_current_path
         
     | 
| 
      
 92 
     | 
    
         
            +
              ].each do |method_name|
         
     | 
| 
      
 93 
     | 
    
         
            +
                CapybaraTestHelpers.define_helper_method(self, method_name, target: :page, assertion: true, inject_test_helper: false)
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
              alias have have_selector
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
              # Public: Allows to check on any input value asynchronously.
         
     | 
| 
      
 99 
     | 
    
         
            +
              def have_value(expected_value, **options)
         
     | 
| 
      
 100 
     | 
    
         
            +
                synchronize_expectation(**options) { expect(value).to_or not_to, eq(expected_value) }
         
     | 
| 
      
 101 
     | 
    
         
            +
                self
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
            private
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              # Internal: Override the method_missing defined in RSpec::Matchers to avoid
         
     | 
| 
      
 107 
     | 
    
         
            +
              # accidentally calling a predicate or has matcher instead of an assertion.
         
     | 
| 
      
 108 
     | 
    
         
            +
              def method_missing(method, *args, **kwargs, &block)
         
     | 
| 
      
 109 
     | 
    
         
            +
                case method.to_s
         
     | 
| 
      
 110 
     | 
    
         
            +
                when CapybaraTestHelpers::TestHelper::DYNAMIC_MATCHER_REGEX
         
     | 
| 
      
 111 
     | 
    
         
            +
                  raise NoMethodError, "undefined method `#{ method }' for #{ inspect }.\nUse `delegate_to_test_context(:#{ method })` in the test helper class if you plan to use it often, or `test_context.#{ method }` as needed in the instance."
         
     | 
| 
      
 112 
     | 
    
         
            +
                else
         
     | 
| 
      
 113 
     | 
    
         
            +
                  super
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
              end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
              # Internal: Override the method_missing defined in RSpec::Matchers to avoid
         
     | 
| 
      
 118 
     | 
    
         
            +
              # accidentally calling a predicate or has matcher instead of an assertion.
         
     | 
| 
      
 119 
     | 
    
         
            +
              def respond_to_missing?(method, *)
         
     | 
| 
      
 120 
     | 
    
         
            +
                return false if method =~ CapybaraTestHelpers::TestHelper::DYNAMIC_MATCHER_REGEX
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                super
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'active_support/concern'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'active_support/core_ext/numeric/time'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'rainbow'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            begin
         
     | 
| 
      
 8 
     | 
    
         
            +
              require 'amazing_print'
         
     | 
| 
      
 9 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            # rubocop:disable Style/ClassVars
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            # Public: Keeps track of the running time for user-defined helpers, useful as a
         
     | 
| 
      
 15 
     | 
    
         
            +
            # way to keep track of the executed methods, and to easily spot slow operations.
         
     | 
| 
      
 16 
     | 
    
         
            +
            module BenchmarkHelpers
         
     | 
| 
      
 17 
     | 
    
         
            +
              extend ActiveSupport::Concern
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              included do
         
     | 
| 
      
 20 
     | 
    
         
            +
                @@indentation_level = 0
         
     | 
| 
      
 21 
     | 
    
         
            +
                @@indented_logs = []
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            protected
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              # Internal: Helper to benchmark an operation, outputs the method name, its
         
     | 
| 
      
 27 
     | 
    
         
            +
              # arguments, and the ellapsed time in milliseconds.
         
     | 
| 
      
 28 
     | 
    
         
            +
              def benchmark_method(method_name, args, kwargs)
         
     | 
| 
      
 29 
     | 
    
         
            +
                @@indented_logs.push(log = +'') # Push it in order, set the content later.
         
     | 
| 
      
 30 
     | 
    
         
            +
                @@indentation_level += 1
         
     | 
| 
      
 31 
     | 
    
         
            +
                before = Time.now
         
     | 
| 
      
 32 
     | 
    
         
            +
                yield
         
     | 
| 
      
 33 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 34 
     | 
    
         
            +
                diff_in_millis = (Time.now - before).in_milliseconds.round
         
     | 
| 
      
 35 
     | 
    
         
            +
                @@indentation_level -= 1
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                # Set the queued message with the method call and the ellapsed time.
         
     | 
| 
      
 38 
     | 
    
         
            +
                log.sub!('', _benchmark_str(method_name: method_name, args: args, kwargs: kwargs, time: diff_in_millis))
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                # Print the messages once we outdent all, and clear the queue.
         
     | 
| 
      
 41 
     | 
    
         
            +
                @@indented_logs.each { |inner_log| Kernel.puts(inner_log) }.clear if @@indentation_level.zero?
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            private
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              # Internal: Indents nested method calls, and adds color to make it readable.
         
     | 
| 
      
 47 
     | 
    
         
            +
              def _benchmark_str(method_name:, args:, kwargs:, time:)
         
     | 
| 
      
 48 
     | 
    
         
            +
                args_str = args.map(&:inspect)
         
     | 
| 
      
 49 
     | 
    
         
            +
                unless kwargs.empty?
         
     | 
| 
      
 50 
     | 
    
         
            +
                  args_str.push kwargs.respond_to?(:awesome_inspect) ? kwargs.awesome_inspect(multiline: false, ruby19_syntax: true)[2..-3] : kwargs.inspect
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
                [
         
     | 
| 
      
 53 
     | 
    
         
            +
                  '  ' * @@indentation_level,
         
     | 
| 
      
 54 
     | 
    
         
            +
                  Rainbow(self.class.name.chomp('TestHelper') + '#').slategray.rjust(40),
         
     | 
| 
      
 55 
     | 
    
         
            +
                  Rainbow(method_name.to_s).cyan,
         
     | 
| 
      
 56 
     | 
    
         
            +
                  Rainbow("(#{ args_str.join(', ') })").slategray,
         
     | 
| 
      
 57 
     | 
    
         
            +
                  '  ',
         
     | 
| 
      
 58 
     | 
    
         
            +
                  Rainbow("#{ time } ms").send(time > 1000 && :red || time > 100 && :yellow || :green),
         
     | 
| 
      
 59 
     | 
    
         
            +
                ].join('')
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              module ClassMethods
         
     | 
| 
      
 63 
     | 
    
         
            +
                # Hook: Benchmarks all methods in the class once it's loaded.
         
     | 
| 
      
 64 
     | 
    
         
            +
                def on_test_helper_load
         
     | 
| 
      
 65 
     | 
    
         
            +
                  super
         
     | 
| 
      
 66 
     | 
    
         
            +
                  benchmark_all
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                # Debug: Wraps all instance methods of the test helper class to log them.
         
     | 
| 
      
 70 
     | 
    
         
            +
                def benchmark_all
         
     | 
| 
      
 71 
     | 
    
         
            +
                  return if defined?(@benchmarked_all)
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  benchmark(instance_methods - superclass.instance_methods - [:lazy_for])
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @benchmarked_all = true
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                # Debug: Wraps a method to output its parameters and ellapsed time.
         
     | 
| 
      
 78 
     | 
    
         
            +
                #
         
     | 
| 
      
 79 
     | 
    
         
            +
                # Usage:
         
     | 
| 
      
 80 
     | 
    
         
            +
                #   benchmark :input_for
         
     | 
| 
      
 81 
     | 
    
         
            +
                #   benchmark def input_for(...)
         
     | 
| 
      
 82 
     | 
    
         
            +
                def benchmark(method_names)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  prepend(Module.new {
         
     | 
| 
      
 84 
     | 
    
         
            +
                    Array.wrap(method_names).each do |method_name|
         
     | 
| 
      
 85 
     | 
    
         
            +
                      define_method(method_name) { |*args, **kwargs, &block|
         
     | 
| 
      
 86 
     | 
    
         
            +
                        benchmark_method(method_name, args, kwargs) { super(*args, **kwargs, &block) }
         
     | 
| 
      
 87 
     | 
    
         
            +
                      }
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  })
         
     | 
| 
      
 90 
     | 
    
         
            +
                  method_names
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
            end
         
     | 
| 
      
 94 
     | 
    
         
            +
            # rubocop:enable Style/ClassVars
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'capybara/rspec'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Internal: Configuration for Provides the basic functionality to create simple test helpers.
         
     | 
| 
      
 6 
     | 
    
         
            +
            module CapybaraTestHelpers
         
     | 
| 
      
 7 
     | 
    
         
            +
              DEFAULTS = {
         
     | 
| 
      
 8 
     | 
    
         
            +
                helpers_paths: ['test_helpers'].freeze,
         
     | 
| 
      
 9 
     | 
    
         
            +
              }.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              # Internal: Reserved methods for Capybara::TestHelper.
         
     | 
| 
      
 12 
     | 
    
         
            +
              test_helper_methods = [
         
     | 
| 
      
 13 
     | 
    
         
            +
                :page,
         
     | 
| 
      
 14 
     | 
    
         
            +
                :find_element,
         
     | 
| 
      
 15 
     | 
    
         
            +
                :should,
         
     | 
| 
      
 16 
     | 
    
         
            +
                :should_not,
         
     | 
| 
      
 17 
     | 
    
         
            +
                :not_to,
         
     | 
| 
      
 18 
     | 
    
         
            +
              ].freeze
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # Internal: Methods that are in the Capybara DSL but are so common that we
         
     | 
| 
      
 21 
     | 
    
         
            +
              # don't want to issue a warning if they are used as selectors.
         
     | 
| 
      
 22 
     | 
    
         
            +
              SKIPPED_DSL_METHODS = [
         
     | 
| 
      
 23 
     | 
    
         
            +
                :title,
         
     | 
| 
      
 24 
     | 
    
         
            +
                :body,
         
     | 
| 
      
 25 
     | 
    
         
            +
                :html,
         
     | 
| 
      
 26 
     | 
    
         
            +
              ].freeze
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              # Internal: Methods that should not be overiden or used as locator aliases to
         
     | 
| 
      
 29 
     | 
    
         
            +
              # avoid confusion while working on test helpers.
         
     | 
| 
      
 30 
     | 
    
         
            +
              RESERVED_METHODS = (Capybara::Session::DSL_METHODS - SKIPPED_DSL_METHODS + test_helper_methods).to_set.freeze
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              # Internal: Ruby 2.7 swallows keyword arguments, so for methods that take a
         
     | 
| 
      
 33 
     | 
    
         
            +
              # Hash as the first argument as well as keyword arguments, we need to manually
         
     | 
| 
      
 34 
     | 
    
         
            +
              # detect and move them to args if empty.
         
     | 
| 
      
 35 
     | 
    
         
            +
              METHODS_EXPECTING_A_HASH = %i[matches_style? has_style? match_style have_style].to_set.freeze
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              # Public: Returns the current configuration for the test helpers.
         
     | 
| 
      
 38 
     | 
    
         
            +
              def self.config
         
     | 
| 
      
 39 
     | 
    
         
            +
                @config ||= OpenStruct.new(DEFAULTS)
         
     | 
| 
      
 40 
     | 
    
         
            +
                yield @config if block_given?
         
     | 
| 
      
 41 
     | 
    
         
            +
                @config
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              # Internal: Allows to define methods that are a part of the Capybara DSL, as
         
     | 
| 
      
 45 
     | 
    
         
            +
              # well as RSpec matchers.
         
     | 
| 
      
 46 
     | 
    
         
            +
              def self.define_helper_method(klass, method_name, wrap: false, assertion: false, target: 'current_context', return_self: assertion, inject_test_helper: true)
         
     | 
| 
      
 47 
     | 
    
         
            +
                klass.class_eval <<~HELPER, __FILE__, __LINE__ + 1
         
     | 
| 
      
 48 
     | 
    
         
            +
                  def #{ method_name }(*args, **kwargs, &filter)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    #{ 'args.push(kwargs) && (kwargs = {}) if args.empty?' if METHODS_EXPECTING_A_HASH.include?(method_name) }
         
     | 
| 
      
 50 
     | 
    
         
            +
                    #{ 'kwargs[:test_helper] = self' if inject_test_helper }
         
     | 
| 
      
 51 
     | 
    
         
            +
                    #{ 'wrap_element ' if wrap }#{ assertion ? "expect(#{ target }).to_or not_to, test_context" : target }.#{ method_name }(*args, **kwargs, &filter)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    #{ 'self' if return_self }
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                HELPER
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'capybara_test_helpers'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            World(CapybaraTestHelpers::DependencyInjection)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            # Public: Use outside of the steps to make it available on all steps.
         
     | 
| 
      
 8 
     | 
    
         
            +
            def use_test_helpers(*names)
         
     | 
| 
      
 9 
     | 
    
         
            +
              names.each do |name|
         
     | 
| 
      
 10 
     | 
    
         
            +
                define_method(name) { get_test_helper(name) }
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Internal: Provides dependency injection for RSpec and Cucumber, by using the
         
     | 
| 
      
 4 
     | 
    
         
            +
            # test helper name and a convention for naming and organizing helpers.
         
     | 
| 
      
 5 
     | 
    
         
            +
            module CapybaraTestHelpers::DependencyInjection
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Public: Returns an instance of a test helper that inherits BaseTestHelper.
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              # NOTE: Memoizes the test helper instances, keeping one per test helper class.
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Test helpers are not mutable, they return a new instance every time an
         
     | 
| 
      
 10 
     | 
    
         
            +
              # operation is performed, so it's safe to apply this optimization.
         
     | 
| 
      
 11 
     | 
    
         
            +
              def get_test_helper(helper_name)
         
     | 
| 
      
 12 
     | 
    
         
            +
                ivar_name = "@#{ helper_name }_capybara_test_helper"
         
     | 
| 
      
 13 
     | 
    
         
            +
                instance_variable_get(ivar_name) ||
         
     | 
| 
      
 14 
     | 
    
         
            +
                  instance_variable_set(ivar_name, get_test_helper_class(helper_name).new(self))
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              # Internal: Requires a test helper file and memoizes the class for all tests.
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              # Returns a Class that subclasses BaseTestHelper.
         
     | 
| 
      
 20 
     | 
    
         
            +
              def get_test_helper_class(name)
         
     | 
| 
      
 21 
     | 
    
         
            +
                file_name = "#{ name }_test_helper"
         
     | 
| 
      
 22 
     | 
    
         
            +
                ivar_name = "@#{ file_name }_test_helper_class"
         
     | 
| 
      
 23 
     | 
    
         
            +
                instance_variable_get(ivar_name) || begin
         
     | 
| 
      
 24 
     | 
    
         
            +
                  require_test_helper(file_name)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  test_helper_class = file_name.camelize.constantize
         
     | 
| 
      
 26 
     | 
    
         
            +
                  test_helper_class.on_test_helper_load
         
     | 
| 
      
 27 
     | 
    
         
            +
                  instance_variable_set(ivar_name, test_helper_class)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              # Internal: Requires a test helper file.
         
     | 
| 
      
 32 
     | 
    
         
            +
              def require_test_helper(name)
         
     | 
| 
      
 33 
     | 
    
         
            +
                CapybaraTestHelpers.config.helpers_paths.each do |path|
         
     | 
| 
      
 34 
     | 
    
         
            +
                  require Pathname.new(File.expand_path(path)).join("#{ name }.rb").to_s
         
     | 
| 
      
 35 
     | 
    
         
            +
                  return true # Don't check on the result, it could have been required earlier.
         
     | 
| 
      
 36 
     | 
    
         
            +
                rescue LoadError
         
     | 
| 
      
 37 
     | 
    
         
            +
                  false
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
                raise LoadError, "No '#{ name }.rb' file found in #{ CapybaraTestHelpers.config.helpers_paths.inspect }. "\
         
     | 
| 
      
 40 
     | 
    
         
            +
                  'Check for typos, or make sure the dirs in `CapybaraTestHelpers.config.helpers_paths` are in the load path.'
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            Capybara.extend(CapybaraTestHelpers::DependencyInjection)
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Internal: Wraps Capybara finders to be aware of the selector aliases, and to
         
     | 
| 
      
 4 
     | 
    
         
            +
            # auto-wrap the returned elements with test helpers.
         
     | 
| 
      
 5 
     | 
    
         
            +
            module CapybaraTestHelpers::Finders
         
     | 
| 
      
 6 
     | 
    
         
            +
              %i[
         
     | 
| 
      
 7 
     | 
    
         
            +
                find
         
     | 
| 
      
 8 
     | 
    
         
            +
                find_all
         
     | 
| 
      
 9 
     | 
    
         
            +
                find_field
         
     | 
| 
      
 10 
     | 
    
         
            +
                find_link
         
     | 
| 
      
 11 
     | 
    
         
            +
                find_button
         
     | 
| 
      
 12 
     | 
    
         
            +
                find_by_id
         
     | 
| 
      
 13 
     | 
    
         
            +
                first
         
     | 
| 
      
 14 
     | 
    
         
            +
                ancestor
         
     | 
| 
      
 15 
     | 
    
         
            +
                sibling
         
     | 
| 
      
 16 
     | 
    
         
            +
              ].each do |method_name|
         
     | 
| 
      
 17 
     | 
    
         
            +
                CapybaraTestHelpers.define_helper_method(self, method_name, wrap: true)
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # Public: Returns all the Capybara nodes that match the specified selector.
         
     | 
| 
      
 21 
     | 
    
         
            +
              #
         
     | 
| 
      
 22 
     | 
    
         
            +
              # Returns an Array of Capybara::Element that match the query.
         
     | 
| 
      
 23 
     | 
    
         
            +
              def all(*args, **kwargs, &filter)
         
     | 
| 
      
 24 
     | 
    
         
            +
                if defined?(::RSpec::Matchers::BuiltIn::All) && args.first.respond_to?(:matches?)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  ::RSpec::Matchers::BuiltIn::All.new(*args, **kwargs)
         
     | 
| 
      
 26 
     | 
    
         
            +
                else
         
     | 
| 
      
 27 
     | 
    
         
            +
                  find_all(*args, **kwargs, &filter)
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              # Internal: Finds an element that matches the specified locator and options.
         
     | 
| 
      
 34 
     | 
    
         
            +
              #
         
     | 
| 
      
 35 
     | 
    
         
            +
              # Returns a Capybara::Node::Element that matches the conditions, or fails.
         
     | 
| 
      
 36 
     | 
    
         
            +
              def find_element(*args, **kwargs, &filter)
         
     | 
| 
      
 37 
     | 
    
         
            +
                kwargs[:test_helper] = self
         
     | 
| 
      
 38 
     | 
    
         
            +
                current_context.find(*args, **kwargs, &filter)
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Internal: Wraps Capybara matchers to enable locator aliases, and to wrap the
         
     | 
| 
      
 4 
     | 
    
         
            +
            # result with a test helper so that methods can be chained in a fluent style.
         
     | 
| 
      
 5 
     | 
    
         
            +
            module CapybaraTestHelpers::Matchers
         
     | 
| 
      
 6 
     | 
    
         
            +
              %i[
         
     | 
| 
      
 7 
     | 
    
         
            +
                has_selector?
         
     | 
| 
      
 8 
     | 
    
         
            +
                has_no_selector?
         
     | 
| 
      
 9 
     | 
    
         
            +
                has_css?
         
     | 
| 
      
 10 
     | 
    
         
            +
                has_no_css?
         
     | 
| 
      
 11 
     | 
    
         
            +
                has_xpath?
         
     | 
| 
      
 12 
     | 
    
         
            +
                has_no_xpath?
         
     | 
| 
      
 13 
     | 
    
         
            +
                has_link?
         
     | 
| 
      
 14 
     | 
    
         
            +
                has_no_link?
         
     | 
| 
      
 15 
     | 
    
         
            +
                has_button?
         
     | 
| 
      
 16 
     | 
    
         
            +
                has_no_button?
         
     | 
| 
      
 17 
     | 
    
         
            +
                has_field?
         
     | 
| 
      
 18 
     | 
    
         
            +
                has_no_field?
         
     | 
| 
      
 19 
     | 
    
         
            +
                has_select?
         
     | 
| 
      
 20 
     | 
    
         
            +
                has_no_select?
         
     | 
| 
      
 21 
     | 
    
         
            +
                has_table?
         
     | 
| 
      
 22 
     | 
    
         
            +
                has_no_table?
         
     | 
| 
      
 23 
     | 
    
         
            +
                has_checked_field?
         
     | 
| 
      
 24 
     | 
    
         
            +
                has_no_checked_field?
         
     | 
| 
      
 25 
     | 
    
         
            +
                has_unchecked_field?
         
     | 
| 
      
 26 
     | 
    
         
            +
                has_no_unchecked_field?
         
     | 
| 
      
 27 
     | 
    
         
            +
                has_title?
         
     | 
| 
      
 28 
     | 
    
         
            +
                has_no_title?
         
     | 
| 
      
 29 
     | 
    
         
            +
                has_title?
         
     | 
| 
      
 30 
     | 
    
         
            +
                has_no_title?
         
     | 
| 
      
 31 
     | 
    
         
            +
              ].each do |method_name|
         
     | 
| 
      
 32 
     | 
    
         
            +
                CapybaraTestHelpers.define_helper_method(self, method_name)
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              %i[
         
     | 
| 
      
 36 
     | 
    
         
            +
                has_ancestor?
         
     | 
| 
      
 37 
     | 
    
         
            +
                has_no_ancestor?
         
     | 
| 
      
 38 
     | 
    
         
            +
                has_sibling?
         
     | 
| 
      
 39 
     | 
    
         
            +
                has_no_sibling?
         
     | 
| 
      
 40 
     | 
    
         
            +
                matches_selector?
         
     | 
| 
      
 41 
     | 
    
         
            +
                not_matches_selector?
         
     | 
| 
      
 42 
     | 
    
         
            +
                matches_css?
         
     | 
| 
      
 43 
     | 
    
         
            +
                not_matches_css?
         
     | 
| 
      
 44 
     | 
    
         
            +
                matches_xpath?
         
     | 
| 
      
 45 
     | 
    
         
            +
                not_matches_xpath?
         
     | 
| 
      
 46 
     | 
    
         
            +
                has_text?
         
     | 
| 
      
 47 
     | 
    
         
            +
                has_no_text?
         
     | 
| 
      
 48 
     | 
    
         
            +
                has_content?
         
     | 
| 
      
 49 
     | 
    
         
            +
                has_no_content?
         
     | 
| 
      
 50 
     | 
    
         
            +
                matches_style?
         
     | 
| 
      
 51 
     | 
    
         
            +
                has_style?
         
     | 
| 
      
 52 
     | 
    
         
            +
              ].each do |method_name|
         
     | 
| 
      
 53 
     | 
    
         
            +
                CapybaraTestHelpers.define_helper_method(self, method_name, target: :to_capybara_node)
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'capybara_test_helpers'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'capybara/rspec'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            # Public: Use in an RSpec describe block or in an included module to make
         
     | 
| 
      
 7 
     | 
    
         
            +
            # helpers available on all specs.
         
     | 
| 
      
 8 
     | 
    
         
            +
            def use_test_helpers(*names)
         
     | 
| 
      
 9 
     | 
    
         
            +
              names.each do |name|
         
     | 
| 
      
 10 
     | 
    
         
            +
                let(name) { get_test_helper(name) }
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 15 
     | 
    
         
            +
              # Make it available only in certain types of tests.
         
     | 
| 
      
 16 
     | 
    
         
            +
              types = %i[feature system view]
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # Options that will register a test helper for the test to use.
         
     | 
| 
      
 19 
     | 
    
         
            +
              keys = %i[capybara_test_helpers test_helpers helpers]
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              # Inject test helpers by using a :helpers or :test_helpers option.
         
     | 
| 
      
 22 
     | 
    
         
            +
              inject_test_helpers = proc { |example|
         
     | 
| 
      
 23 
     | 
    
         
            +
                keys.flat_map { |key| example.metadata[key] }.compact.each do |name|
         
     | 
| 
      
 24 
     | 
    
         
            +
                  example.example_group_instance.define_singleton_method(name) { get_test_helper(name) }
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              }
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              # Allow injecting test helpers in a feature or scenario.
         
     | 
| 
      
 29 
     | 
    
         
            +
              %i[feature system view].each do |type|
         
     | 
| 
      
 30 
     | 
    
         
            +
                config.include(CapybaraTestHelpers::DependencyInjection, type: type)
         
     | 
| 
      
 31 
     | 
    
         
            +
                config.before(:each, type: type, &inject_test_helpers)
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     |