File: browser.rb

package info (click to toggle)
ruby-capybara 3.36.0%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 2,380 kB
  • sloc: ruby: 23,399; javascript: 748; makefile: 11
file content (144 lines) | stat: -rw-r--r-- 3,664 bytes parent folder | download
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