bjeanes-capybara 0.3.1
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.
- data/History.txt +4 -0
- data/Manifest.txt +87 -0
- data/README.rdoc +404 -0
- data/Rakefile +29 -0
- data/config.ru +6 -0
- data/lib/capybara.rb +53 -0
- data/lib/capybara/cucumber.rb +32 -0
- data/lib/capybara/driver/base.rb +37 -0
- data/lib/capybara/driver/celerity_driver.rb +135 -0
- data/lib/capybara/driver/culerity_driver.rb +25 -0
- data/lib/capybara/driver/rack_test_driver.rb +244 -0
- data/lib/capybara/driver/selenium_driver.rb +137 -0
- data/lib/capybara/dsl.rb +60 -0
- data/lib/capybara/node.rb +69 -0
- data/lib/capybara/rails.rb +11 -0
- data/lib/capybara/save_and_open_page.rb +33 -0
- data/lib/capybara/searchable.rb +53 -0
- data/lib/capybara/server.rb +111 -0
- data/lib/capybara/session.rb +274 -0
- data/lib/capybara/wait_until.rb +23 -0
- data/lib/capybara/xpath.rb +176 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/capybara_spec.rb +18 -0
- data/spec/driver/celerity_driver_spec.rb +17 -0
- data/spec/driver/culerity_driver_spec.rb +13 -0
- data/spec/driver/rack_test_driver_spec.rb +12 -0
- data/spec/driver/remote_culerity_driver_spec.rb +26 -0
- data/spec/driver/remote_selenium_driver_spec.rb +18 -0
- data/spec/driver/selenium_driver_spec.rb +12 -0
- data/spec/drivers_spec.rb +139 -0
- data/spec/dsl/all_spec.rb +69 -0
- data/spec/dsl/attach_file_spec.rb +64 -0
- data/spec/dsl/check_spec.rb +39 -0
- data/spec/dsl/choose_spec.rb +26 -0
- data/spec/dsl/click_button_spec.rb +218 -0
- data/spec/dsl/click_link_spec.rb +108 -0
- data/spec/dsl/click_spec.rb +24 -0
- data/spec/dsl/current_url_spec.rb +8 -0
- data/spec/dsl/fill_in_spec.rb +91 -0
- data/spec/dsl/find_button_spec.rb +16 -0
- data/spec/dsl/find_by_id_spec.rb +16 -0
- data/spec/dsl/find_field_spec.rb +22 -0
- data/spec/dsl/find_link_spec.rb +17 -0
- data/spec/dsl/find_spec.rb +57 -0
- data/spec/dsl/has_button_spec.rb +32 -0
- data/spec/dsl/has_content_spec.rb +101 -0
- data/spec/dsl/has_css_spec.rb +107 -0
- data/spec/dsl/has_field_spec.rb +96 -0
- data/spec/dsl/has_link_spec.rb +33 -0
- data/spec/dsl/has_xpath_spec.rb +123 -0
- data/spec/dsl/locate_spec.rb +59 -0
- data/spec/dsl/select_spec.rb +71 -0
- data/spec/dsl/uncheck_spec.rb +21 -0
- data/spec/dsl/within_spec.rb +153 -0
- data/spec/dsl_spec.rb +140 -0
- data/spec/fixtures/capybara.jpg +0 -0
- data/spec/fixtures/test_file.txt +1 -0
- data/spec/public/jquery-ui.js +35 -0
- data/spec/public/jquery.js +19 -0
- data/spec/public/test.js +30 -0
- data/spec/save_and_open_page_spec.rb +43 -0
- data/spec/searchable_spec.rb +61 -0
- data/spec/server_spec.rb +47 -0
- data/spec/session/celerity_session_spec.rb +27 -0
- data/spec/session/culerity_session_spec.rb +25 -0
- data/spec/session/rack_test_session_spec.rb +25 -0
- data/spec/session/selenium_session_spec.rb +25 -0
- data/spec/session_spec.rb +77 -0
- data/spec/session_with_headers_support_spec.rb +13 -0
- data/spec/session_with_javascript_support_spec.rb +182 -0
- data/spec/session_without_headers_support_spec.rb +15 -0
- data/spec/session_without_javascript_support_spec.rb +15 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/test_app.rb +71 -0
- data/spec/views/buttons.erb +4 -0
- data/spec/views/fieldsets.erb +29 -0
- data/spec/views/form.erb +226 -0
- data/spec/views/postback.erb +13 -0
- data/spec/views/tables.erb +122 -0
- data/spec/views/with_html.erb +38 -0
- data/spec/views/with_js.erb +34 -0
- data/spec/views/with_scope.erb +36 -0
- data/spec/views/with_simple_html.erb +1 -0
- data/spec/wait_until_spec.rb +28 -0
- data/spec/xpath_spec.rb +271 -0
- metadata +239 -0
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'hoe', '>= 2.1.0'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :newgem
|
7
|
+
|
8
|
+
# Generate all the Rake tasks
|
9
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
10
|
+
Hoe.spec 'bjeanes-capybara' do
|
11
|
+
developer 'Jonas Nicklas', 'jonas.nicklas@gmail.com'
|
12
|
+
|
13
|
+
self.readme_file = 'README.rdoc'
|
14
|
+
self.extra_rdoc_files = Dir['*.rdoc']
|
15
|
+
|
16
|
+
self.extra_deps = [
|
17
|
+
['nokogiri', '>= 1.3.3'],
|
18
|
+
['mime-types', '>= 1.16'],
|
19
|
+
['culerity', '>= 0.2.4'],
|
20
|
+
['selenium-webdriver', '>= 0.0.3'],
|
21
|
+
['rack', '>= 1.0.0'],
|
22
|
+
['rack-test', '>= 0.5.2'],
|
23
|
+
]
|
24
|
+
|
25
|
+
self.extra_dev_deps = [
|
26
|
+
['sinatra', '>= 0.9.4'],
|
27
|
+
['rspec', '>= 1.2.9']
|
28
|
+
]
|
29
|
+
end
|
data/config.ru
ADDED
data/lib/capybara.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Capybara
|
5
|
+
VERSION = '0.3.1'
|
6
|
+
|
7
|
+
class CapybaraError < StandardError; end
|
8
|
+
class DriverNotFoundError < CapybaraError; end
|
9
|
+
class ElementNotFound < CapybaraError; end
|
10
|
+
class OptionNotFound < ElementNotFound; end
|
11
|
+
class UnselectNotAllowed < CapybaraError; end
|
12
|
+
class NotSupportedByDriverError < CapybaraError; end
|
13
|
+
class TimeoutError < CapybaraError; end
|
14
|
+
class LocateHiddenElementError < CapybaraError; end
|
15
|
+
class InfiniteRedirectError < TimeoutError; end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :debug, :asset_root, :app_host, :run_server
|
19
|
+
attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements
|
20
|
+
|
21
|
+
def default_selector
|
22
|
+
@default_selector ||= :xpath
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_wait_time
|
26
|
+
@default_wait_time ||= 2
|
27
|
+
end
|
28
|
+
|
29
|
+
def log(message)
|
30
|
+
puts "[capybara] #{message}" if debug
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
autoload :Server, 'capybara/server'
|
36
|
+
autoload :Session, 'capybara/session'
|
37
|
+
autoload :Node, 'capybara/node'
|
38
|
+
autoload :XPath, 'capybara/xpath'
|
39
|
+
autoload :Searchable, 'capybara/searchable'
|
40
|
+
|
41
|
+
module Driver
|
42
|
+
autoload :Base, 'capybara/driver/base'
|
43
|
+
autoload :RackTest, 'capybara/driver/rack_test_driver'
|
44
|
+
autoload :Celerity, 'capybara/driver/celerity_driver'
|
45
|
+
autoload :Culerity, 'capybara/driver/culerity_driver'
|
46
|
+
autoload :Selenium, 'capybara/driver/selenium_driver'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Capybara.run_server = true
|
51
|
+
Capybara.default_selector = :xpath
|
52
|
+
Capybara.default_wait_time = 2
|
53
|
+
Capybara.ignore_hidden_elements = false
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'capybara/dsl'
|
3
|
+
|
4
|
+
World(Capybara)
|
5
|
+
|
6
|
+
After do
|
7
|
+
Capybara.reset_sessions!
|
8
|
+
end
|
9
|
+
|
10
|
+
Before('@javascript') do
|
11
|
+
Capybara.current_driver = Capybara.javascript_driver
|
12
|
+
end
|
13
|
+
|
14
|
+
Before('@selenium') do
|
15
|
+
Capybara.current_driver = :selenium
|
16
|
+
end
|
17
|
+
|
18
|
+
Before('@celerity') do
|
19
|
+
Capybara.current_driver = :celerity
|
20
|
+
end
|
21
|
+
|
22
|
+
Before('@culerity') do
|
23
|
+
Capybara.current_driver = :culerity
|
24
|
+
end
|
25
|
+
|
26
|
+
Before('@rack_test') do
|
27
|
+
Capybara.current_driver = :rack_test
|
28
|
+
end
|
29
|
+
|
30
|
+
After do
|
31
|
+
Capybara.use_default_driver
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Capybara::Driver::Base
|
2
|
+
def current_url
|
3
|
+
raise NotImplementedError
|
4
|
+
end
|
5
|
+
|
6
|
+
def visit(path)
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(query)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate_script(script)
|
15
|
+
raise Capybara::NotSupportedByDriverError
|
16
|
+
end
|
17
|
+
|
18
|
+
def wait?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def response_headers
|
23
|
+
raise Capybara::NotSupportedByDriverError
|
24
|
+
end
|
25
|
+
|
26
|
+
def body
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
def source
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
def cleanup!
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
class Capybara::Driver::Celerity < Capybara::Driver::Base
|
2
|
+
class Node < Capybara::Node
|
3
|
+
def text
|
4
|
+
node.text
|
5
|
+
end
|
6
|
+
|
7
|
+
def [](name)
|
8
|
+
value = if name.to_sym == :class
|
9
|
+
node.class_name
|
10
|
+
else
|
11
|
+
node.send(name.to_sym)
|
12
|
+
end
|
13
|
+
return value if value and not value.to_s.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def value
|
17
|
+
if node.type == 'select-multiple'
|
18
|
+
node.selected_options
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def set(value)
|
25
|
+
node.set(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def select(option)
|
29
|
+
node.select(option)
|
30
|
+
rescue
|
31
|
+
options = all(:xpath, "//option").map { |o| "'#{o.text}'" }.join(', ')
|
32
|
+
raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def unselect(option)
|
36
|
+
unless node.multiple?
|
37
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
|
38
|
+
end
|
39
|
+
|
40
|
+
# FIXME: couldn't find a clean way to unselect, so clear and reselect
|
41
|
+
selected_options = node.selected_options
|
42
|
+
if unselect_option = selected_options.detect { |value| value == option } ||
|
43
|
+
selected_options.detect { |value| value.index(option) }
|
44
|
+
node.clear
|
45
|
+
(selected_options - [unselect_option]).each { |value| node.select_value(value) }
|
46
|
+
else
|
47
|
+
options = all(:xpath, "//option").map { |o| "'#{o.text}'" }.join(', ')
|
48
|
+
raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def click
|
53
|
+
node.click
|
54
|
+
end
|
55
|
+
|
56
|
+
def drag_to(element)
|
57
|
+
node.fire_event('mousedown')
|
58
|
+
element.node.fire_event('mousemove')
|
59
|
+
element.node.fire_event('mouseup')
|
60
|
+
end
|
61
|
+
|
62
|
+
def tag_name
|
63
|
+
# FIXME: this might be the dumbest way ever of getting the tag name
|
64
|
+
# there has to be something better...
|
65
|
+
node.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1]
|
66
|
+
end
|
67
|
+
|
68
|
+
def visible?
|
69
|
+
node.visible?
|
70
|
+
end
|
71
|
+
|
72
|
+
def path
|
73
|
+
node.xpath
|
74
|
+
end
|
75
|
+
|
76
|
+
def trigger(event)
|
77
|
+
node.fire_event(event.to_s)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
attr_reader :app, :rack_server
|
83
|
+
|
84
|
+
def initialize(app)
|
85
|
+
@app = app
|
86
|
+
@rack_server = Capybara::Server.new(@app)
|
87
|
+
@rack_server.boot if Capybara.run_server
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit(path)
|
91
|
+
browser.goto(url(path))
|
92
|
+
end
|
93
|
+
|
94
|
+
def current_url
|
95
|
+
browser.url
|
96
|
+
end
|
97
|
+
|
98
|
+
def source
|
99
|
+
browser.html
|
100
|
+
end
|
101
|
+
|
102
|
+
def body
|
103
|
+
browser.document.as_xml
|
104
|
+
end
|
105
|
+
|
106
|
+
def response_headers
|
107
|
+
browser.response_headers
|
108
|
+
end
|
109
|
+
|
110
|
+
def find(selector)
|
111
|
+
browser.elements_by_xpath(selector).map { |node| Node.new(self, node) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def wait?; true; end
|
115
|
+
|
116
|
+
def evaluate_script(script)
|
117
|
+
browser.execute_script "#{script}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def browser
|
121
|
+
unless @_browser
|
122
|
+
require 'celerity'
|
123
|
+
@_browser = ::Celerity::Browser.new(:browser => :firefox, :log_level => :off)
|
124
|
+
end
|
125
|
+
|
126
|
+
@_browser
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def url(path)
|
132
|
+
rack_server.url(path)
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'culerity'
|
2
|
+
|
3
|
+
class Capybara::Driver::Culerity < Capybara::Driver::Celerity
|
4
|
+
|
5
|
+
def self.server
|
6
|
+
unless @_server
|
7
|
+
@_server = ::Culerity::run_server
|
8
|
+
at_exit do
|
9
|
+
@_server.close
|
10
|
+
end
|
11
|
+
end
|
12
|
+
@_server
|
13
|
+
end
|
14
|
+
|
15
|
+
def browser
|
16
|
+
unless @_browser
|
17
|
+
@_browser = ::Culerity::RemoteBrowserProxy.new self.class.server, {:browser => :firefox, :log_level => :off}
|
18
|
+
at_exit do
|
19
|
+
@_browser.exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@_browser
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
require 'mime/types'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
class Capybara::Driver::RackTest < Capybara::Driver::Base
|
7
|
+
class Node < Capybara::Node
|
8
|
+
def text
|
9
|
+
node.text
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](name)
|
13
|
+
attr_name = name.to_s
|
14
|
+
case
|
15
|
+
when 'select' == tag_name && 'value' == attr_name
|
16
|
+
if node['multiple'] == 'multiple'
|
17
|
+
node.xpath(".//option[@selected='selected']").map { |option| option.content }
|
18
|
+
else
|
19
|
+
option = node.xpath(".//option[@selected='selected']").first || node.xpath(".//option").first
|
20
|
+
option.content if option
|
21
|
+
end
|
22
|
+
when 'input' == tag_name && 'checkbox' == type && 'checked' == attr_name
|
23
|
+
node[attr_name] == 'checked' ? true : false
|
24
|
+
else
|
25
|
+
node[attr_name]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def set(value)
|
31
|
+
if tag_name == 'input' and %w(text password hidden file).include?(type)
|
32
|
+
node['value'] = value.to_s
|
33
|
+
elsif tag_name == 'input' and type == 'radio'
|
34
|
+
driver.html.xpath("//input[@name='#{self[:name]}']").each { |node| node.remove_attribute("checked") }
|
35
|
+
node['checked'] = 'checked'
|
36
|
+
elsif tag_name == 'input' and type == 'checkbox'
|
37
|
+
if value
|
38
|
+
node['checked'] = 'checked'
|
39
|
+
else
|
40
|
+
node.remove_attribute('checked')
|
41
|
+
end
|
42
|
+
elsif tag_name == "textarea"
|
43
|
+
node.content = value.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def select(option)
|
48
|
+
if node['multiple'] != 'multiple'
|
49
|
+
node.xpath(".//option[@selected]").each { |node| node.remove_attribute("selected") }
|
50
|
+
end
|
51
|
+
|
52
|
+
if option_node = node.xpath(".//option[text()='#{option}']").first ||
|
53
|
+
node.xpath(".//option[contains(.,'#{option}')]").first
|
54
|
+
option_node["selected"] = 'selected'
|
55
|
+
else
|
56
|
+
options = node.xpath(".//option").map { |o| "'#{o.text}'" }.join(', ')
|
57
|
+
raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def unselect(option)
|
62
|
+
if node['multiple'] != 'multiple'
|
63
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option '#{option}' from single select box."
|
64
|
+
end
|
65
|
+
|
66
|
+
if option_node = node.xpath(".//option[text()='#{option}']").first ||
|
67
|
+
node.xpath(".//option[contains(.,'#{option}')]").first
|
68
|
+
option_node.remove_attribute('selected')
|
69
|
+
else
|
70
|
+
options = node.xpath(".//option").map { |o| "'#{o.text}'" }.join(', ')
|
71
|
+
raise Capybara::OptionNotFound, "No such option '#{option}' in this select box. Available options: #{options}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def click
|
76
|
+
if tag_name == 'a'
|
77
|
+
driver.visit(self[:href].to_s)
|
78
|
+
elsif (tag_name == 'input' or tag_name == 'button') and %w(submit image).include?(type)
|
79
|
+
Form.new(driver, form).submit(self)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def tag_name
|
84
|
+
node.node_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def visible?
|
88
|
+
node.xpath("./ancestor-or-self::*[contains(@style, 'display:none') or contains(@style, 'display: none')]").size == 0
|
89
|
+
end
|
90
|
+
|
91
|
+
def path
|
92
|
+
node.path
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def type
|
98
|
+
node[:type]
|
99
|
+
end
|
100
|
+
|
101
|
+
def form
|
102
|
+
node.ancestors('form').first
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Form < Node
|
107
|
+
def params(button)
|
108
|
+
params = {}
|
109
|
+
|
110
|
+
text_fields = %w[text hidden password url color tel email search].map{|f| "@type='#{f}'"}.join(' or ')
|
111
|
+
|
112
|
+
node.xpath(".//input[#{text_fields}]").map do |input|
|
113
|
+
merge_param!(params, input['name'].to_s, input['value'].to_s)
|
114
|
+
end
|
115
|
+
node.xpath(".//textarea").map do |textarea|
|
116
|
+
merge_param!(params, textarea['name'].to_s, textarea.text.to_s)
|
117
|
+
end
|
118
|
+
node.xpath(".//input[@type='radio' or @type='checkbox']").map do |input|
|
119
|
+
merge_param!(params, input['name'].to_s, input['value'].to_s) if input['checked']
|
120
|
+
end
|
121
|
+
node.xpath(".//select").map do |select|
|
122
|
+
if select['multiple'] == 'multiple'
|
123
|
+
options = select.xpath(".//option[@selected]")
|
124
|
+
options.each do |option|
|
125
|
+
merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
option = select.xpath(".//option[@selected]").first
|
129
|
+
option ||= select.xpath('.//option').first
|
130
|
+
merge_param!(params, select['name'].to_s, (option['value'] || option.text).to_s) if option
|
131
|
+
end
|
132
|
+
end
|
133
|
+
node.xpath(".//input[@type='file']").map do |input|
|
134
|
+
unless input['value'].to_s.empty?
|
135
|
+
if multipart?
|
136
|
+
content_type = MIME::Types.type_for(input['value'].to_s).first.to_s
|
137
|
+
file = Rack::Test::UploadedFile.new(input['value'].to_s, content_type)
|
138
|
+
merge_param!(params, input['name'].to_s, file)
|
139
|
+
else
|
140
|
+
merge_param!(params, input['name'].to_s, File.basename(input['value'].to_s))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
merge_param!(params, button[:name], button[:value]) if button[:name]
|
145
|
+
params
|
146
|
+
end
|
147
|
+
|
148
|
+
def submit(button)
|
149
|
+
driver.submit(method, node['action'].to_s, params(button))
|
150
|
+
end
|
151
|
+
|
152
|
+
def multipart?
|
153
|
+
self[:enctype] == "multipart/form-data"
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def method
|
159
|
+
self[:method] =~ /post/i ? :post : :get
|
160
|
+
end
|
161
|
+
|
162
|
+
def merge_param!(params, key, value)
|
163
|
+
collection = key.sub!(/\[\]$/, '')
|
164
|
+
if collection
|
165
|
+
if params[key]
|
166
|
+
params[key] << value
|
167
|
+
else
|
168
|
+
params[key] = [value]
|
169
|
+
end
|
170
|
+
else
|
171
|
+
params[key] = value
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
include ::Rack::Test::Methods
|
177
|
+
attr_reader :app, :html, :body
|
178
|
+
|
179
|
+
alias_method :response, :last_response
|
180
|
+
alias_method :request, :last_request
|
181
|
+
alias_method :source, :body
|
182
|
+
|
183
|
+
def initialize(app)
|
184
|
+
@app = app
|
185
|
+
end
|
186
|
+
|
187
|
+
def visit(path, attributes = {})
|
188
|
+
return if path.gsub(/^#{current_path}/, '') =~ /^#/
|
189
|
+
get(path, attributes, env)
|
190
|
+
follow_redirects!
|
191
|
+
cache_body
|
192
|
+
end
|
193
|
+
|
194
|
+
def current_url
|
195
|
+
request.url rescue ""
|
196
|
+
end
|
197
|
+
|
198
|
+
def response_headers
|
199
|
+
response.headers
|
200
|
+
end
|
201
|
+
|
202
|
+
def submit(method, path, attributes)
|
203
|
+
path = current_path if not path or path.empty?
|
204
|
+
send(method, path, attributes, env)
|
205
|
+
follow_redirects!
|
206
|
+
cache_body
|
207
|
+
end
|
208
|
+
|
209
|
+
def find(selector)
|
210
|
+
html.xpath(selector).map { |node| Node.new(self, node) }
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def current_path
|
216
|
+
request.path rescue ""
|
217
|
+
end
|
218
|
+
|
219
|
+
def follow_redirects!
|
220
|
+
Capybara::WaitUntil.timeout(4) do
|
221
|
+
redirect = response.redirect?
|
222
|
+
follow_redirect! if redirect
|
223
|
+
not redirect
|
224
|
+
end
|
225
|
+
rescue Capybara::TimeoutError
|
226
|
+
raise Capybara::InfiniteRedirectError, "infinite redirect detected!"
|
227
|
+
end
|
228
|
+
|
229
|
+
def env
|
230
|
+
env = {}
|
231
|
+
begin
|
232
|
+
env["HTTP_REFERER"] = request.url
|
233
|
+
rescue Rack::Test::Error
|
234
|
+
# no request yet
|
235
|
+
end
|
236
|
+
env
|
237
|
+
end
|
238
|
+
|
239
|
+
def cache_body
|
240
|
+
@body = response.body
|
241
|
+
@html = Nokogiri::HTML(body)
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|