1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
# frozen_string_literal: true
class Capybara::RackTest::Browser
include ::Rack::Test::Methods
attr_reader :driver
attr_accessor :current_host
def initialize(driver)
@driver = driver
@current_fragment = nil
end
def app
driver.app
end
def options
driver.options
end
def visit(path, **attributes)
reset_host!
process_and_follow_redirects(:get, path, attributes)
end
def refresh
reset_cache!
request(last_request.fullpath, last_request.env)
end
def submit(method, path, attributes)
path = request_path if path.nil? || path.empty?
uri = build_uri(path)
uri.query = '' if method.to_s.casecmp('get').zero?
process_and_follow_redirects(method, uri.to_s, attributes, 'HTTP_REFERER' => current_url)
end
def follow(method, path, **attributes)
return if fragment_or_script?(path)
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => current_url)
end
def process_and_follow_redirects(method, path, attributes = {}, env = {})
@current_fragment = build_uri(path).fragment
process(method, path, attributes, env)
return unless driver.follow_redirects?
driver.redirect_limit.times do
if last_response.redirect?
if [307, 308].include? last_response.status
process(last_request.request_method, last_response['Location'], last_request.params, env)
else
process(:get, last_response['Location'], {}, env)
end
end
end
if last_response.redirect? # rubocop:disable Style/GuardClause
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects."
end
end
def process(method, path, attributes = {}, env = {})
method = method.downcase
new_uri = build_uri(path)
@current_scheme, @current_host, @current_port = new_uri.select(:scheme, :host, :port)
@current_fragment = new_uri.fragment || @current_fragment
reset_cache!
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
end
def build_uri(path)
URI.parse(path).tap do |uri|
uri.path = request_path if path.empty? || path.start_with?('?')
uri.path = '/' if uri.path.empty?
uri.path = request_path.sub(%r{/[^/]*$}, '/') + uri.path unless uri.path.start_with?('/')
uri.scheme ||= @current_scheme
uri.host ||= @current_host
uri.port ||= @current_port unless uri.default_port == @current_port
end
end
def current_url
uri = build_uri(last_request.url)
uri.fragment = @current_fragment if @current_fragment
uri.to_s
rescue Rack::Test::Error
''
end
def reset_host!
uri = URI.parse(driver.session_options.app_host || driver.session_options.default_host)
@current_scheme, @current_host, @current_port = uri.select(:scheme, :host, :port)
end
def reset_cache!
@dom = nil
end
def dom
@dom ||= Capybara::HTML(html)
end
def find(format, selector)
if format == :css
dom.css(selector, Capybara::RackTest::CSSHandlers.new)
else
dom.xpath(selector)
end.map { |node| Capybara::RackTest::Node.new(self, node) }
end
def html
last_response.body
rescue Rack::Test::Error
''
end
def title
dom.title
end
protected
def build_rack_mock_session
reset_host! unless current_host
Rack::MockSession.new(app, current_host)
end
def request_path
last_request.path
rescue Rack::Test::Error
'/'
end
private
def fragment_or_script?(path)
path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
end
end
|