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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
# 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)
@new_visit_request = true
reset_cache!
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, content_type: nil)
path = request_path if path.nil? || path.empty?
uri = build_uri(path)
uri.query = '' if method.to_s.casecmp('get').zero?
env = { 'HTTP_REFERER' => referer_url }
env['CONTENT_TYPE'] = content_type if content_type
process_and_follow_redirects(
method,
uri.to_s,
attributes,
env
)
end
def follow(method, path, **attributes)
return if fragment_or_script?(path)
process_and_follow_redirects(method, path, attributes, 'HTTP_REFERER' => referer_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!
@new_visit_request = false
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
end
def build_uri(path)
uri = URI.parse(path)
base_uri = base_relative_uri_for(uri)
uri.path = base_uri.path + uri.path unless uri.absolute? || uri.path.start_with?('/')
if base_uri.absolute?
base_uri.merge(uri)
else
uri.scheme ||= @current_scheme
uri.host ||= @current_host
uri.port ||= @current_port unless uri.default_port == @current_port
uri
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
def last_request
raise Rack::Test::Error if @new_visit_request
super
end
def last_response
raise Rack::Test::Error if @new_visit_request
super
end
protected
def base_href
find(:css, 'head > base').first&.[](:href).to_s
end
def base_relative_uri_for(uri)
base_uri = URI.parse(base_href)
current_uri = URI.parse(safe_last_request&.url.to_s).tap do |c|
c.path.sub!(%r{/[^/]*$}, '/') unless uri.path.empty?
c.path = '/' if c.path.empty?
end
if [current_uri, base_uri].any?(&:absolute?)
current_uri.merge(base_uri)
else
base_uri.path = current_uri.path if base_uri.path.empty?
base_uri
end
end
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
def safe_last_request
last_request
rescue Rack::Test::Error
nil
end
private
def fragment_or_script?(path)
path.gsub(/^#{Regexp.escape(request_path)}/, '').start_with?('#') || path.downcase.start_with?('javascript:')
end
def referer_url
build_uri(last_request.url).to_s
rescue Rack::Test::Error
''
end
end
|