/
browser.rb
179 lines (144 loc) · 4.23 KB
/
browser.rb
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
# 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' => referer_url)
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!
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
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