capybara 0.4.1.2 → 1.0.0.beta1
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 +46 -0
- data/README.rdoc +211 -64
- data/lib/capybara.rb +31 -15
- data/lib/capybara/cucumber.rb +12 -16
- data/lib/capybara/dsl.rb +65 -28
- data/lib/capybara/node/actions.rb +7 -5
- data/lib/capybara/node/document.rb +8 -0
- data/lib/capybara/node/finders.rb +11 -7
- data/lib/capybara/node/matchers.rb +32 -6
- data/lib/capybara/node/simple.rb +20 -0
- data/lib/capybara/rack_test/browser.rb +115 -0
- data/lib/capybara/rack_test/driver.rb +77 -0
- data/lib/capybara/rack_test/form.rb +80 -0
- data/lib/capybara/rack_test/node.rb +101 -0
- data/lib/capybara/rspec.rb +11 -3
- data/lib/capybara/rspec/features.rb +22 -0
- data/lib/capybara/rspec/matchers.rb +146 -0
- data/lib/capybara/selector.rb +27 -8
- data/lib/capybara/selenium/driver.rb +148 -0
- data/lib/capybara/selenium/node.rb +91 -0
- data/lib/capybara/session.rb +42 -15
- data/lib/capybara/spec/driver.rb +55 -1
- data/lib/capybara/spec/fixtures/capybara.jpg +0 -0
- data/lib/capybara/spec/public/test.js +7 -2
- data/lib/capybara/spec/session.rb +51 -7
- data/lib/capybara/spec/session/attach_file_spec.rb +9 -6
- data/lib/capybara/spec/session/click_button_spec.rb +35 -0
- data/lib/capybara/spec/session/current_host_spec.rb +62 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
- data/lib/capybara/spec/session/find_spec.rb +23 -1
- data/lib/capybara/spec/session/first_spec.rb +39 -6
- data/lib/capybara/spec/session/has_css_spec.rb +30 -0
- data/lib/capybara/spec/session/has_field_spec.rb +47 -11
- data/lib/capybara/spec/session/javascript.rb +0 -1
- data/lib/capybara/spec/session/text_spec.rb +19 -0
- data/lib/capybara/spec/test_app.rb +9 -0
- data/lib/capybara/spec/views/form.erb +8 -3
- data/lib/capybara/spec/views/header_links.erb +7 -0
- data/lib/capybara/spec/views/host_links.erb +12 -0
- data/lib/capybara/spec/views/with_html.erb +6 -2
- data/lib/capybara/spec/views/with_html_entities.erb +1 -0
- data/lib/capybara/spec/views/with_js.erb +4 -0
- data/lib/capybara/util/save_and_open_page.rb +7 -3
- data/lib/capybara/util/timeout.rb +2 -2
- data/lib/capybara/version.rb +1 -1
- data/spec/capybara_spec.rb +1 -1
- data/spec/driver/rack_test_driver_spec.rb +24 -2
- data/spec/driver/selenium_driver_spec.rb +2 -1
- data/spec/dsl_spec.rb +56 -4
- data/spec/rspec/features_spec.rb +45 -0
- data/spec/rspec/matchers_spec.rb +451 -0
- data/spec/rspec_spec.rb +9 -2
- data/spec/save_and_open_page_spec.rb +9 -13
- data/spec/server_spec.rb +4 -0
- data/spec/session/rack_test_session_spec.rb +2 -2
- data/spec/session/selenium_session_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -14
- data/spec/string_spec.rb +1 -1
- metadata +60 -69
- data/lib/capybara/driver/celerity_driver.rb +0 -164
- data/lib/capybara/driver/culerity_driver.rb +0 -26
- data/lib/capybara/driver/rack_test_driver.rb +0 -303
- data/lib/capybara/driver/selenium_driver.rb +0 -161
- data/spec/driver/celerity_driver_spec.rb +0 -13
- data/spec/driver/culerity_driver_spec.rb +0 -14
- data/spec/driver/remote_culerity_driver_spec.rb +0 -22
- data/spec/driver/remote_selenium_driver_spec.rb +0 -16
- data/spec/session/celerity_session_spec.rb +0 -24
- data/spec/session/culerity_session_spec.rb +0 -26
@@ -1,164 +0,0 @@
|
|
1
|
-
class Capybara::Driver::Celerity < Capybara::Driver::Base
|
2
|
-
class Node < Capybara::Driver::Node
|
3
|
-
def text
|
4
|
-
native.text
|
5
|
-
end
|
6
|
-
|
7
|
-
def [](name)
|
8
|
-
value = native.attribute_value(name.to_sym)
|
9
|
-
return value if value and not value.to_s.empty?
|
10
|
-
end
|
11
|
-
|
12
|
-
def value
|
13
|
-
if tag_name == "select" and native.multiple?
|
14
|
-
find(".//option[@selected]").map { |n| if n.has_value? then n.value else n.text end }
|
15
|
-
else
|
16
|
-
native.value
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def set(value)
|
21
|
-
native.set(value)
|
22
|
-
end
|
23
|
-
|
24
|
-
def select_option
|
25
|
-
native.click
|
26
|
-
end
|
27
|
-
|
28
|
-
def unselect_option
|
29
|
-
unless select_node.native.multiple?
|
30
|
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
31
|
-
end
|
32
|
-
|
33
|
-
# FIXME: couldn't find a clean way to unselect, so clear and reselect
|
34
|
-
selected_nodes = select_node.find('.//option[@selected]')
|
35
|
-
select_node.native.clear
|
36
|
-
selected_nodes.each { |n| n.click unless n.path == path }
|
37
|
-
end
|
38
|
-
|
39
|
-
def click
|
40
|
-
native.click
|
41
|
-
end
|
42
|
-
|
43
|
-
def drag_to(element)
|
44
|
-
native.fire_event('mousedown')
|
45
|
-
element.native.fire_event('mousemove')
|
46
|
-
element.native.fire_event('mouseup')
|
47
|
-
end
|
48
|
-
|
49
|
-
def tag_name
|
50
|
-
# FIXME: this might be the dumbest way ever of getting the tag name
|
51
|
-
# there has to be something better...
|
52
|
-
native.to_xml[/^\s*<([a-z0-9\-\:]+)/, 1]
|
53
|
-
end
|
54
|
-
|
55
|
-
def visible?
|
56
|
-
native.visible?
|
57
|
-
end
|
58
|
-
|
59
|
-
def checked?
|
60
|
-
native.checked?
|
61
|
-
rescue # https://github.com/langalex/culerity/issues/issue/33
|
62
|
-
false
|
63
|
-
end
|
64
|
-
|
65
|
-
def selected?
|
66
|
-
native.selected?
|
67
|
-
rescue # https://github.com/langalex/culerity/issues/issue/33
|
68
|
-
false
|
69
|
-
end
|
70
|
-
|
71
|
-
def path
|
72
|
-
native.xpath
|
73
|
-
end
|
74
|
-
|
75
|
-
def trigger(event)
|
76
|
-
native.fire_event(event.to_s)
|
77
|
-
end
|
78
|
-
|
79
|
-
def find(locator)
|
80
|
-
noko_node = Nokogiri::HTML(driver.body).xpath(native.xpath).first
|
81
|
-
all_nodes = noko_node.xpath(locator).map { |n| n.path }.join(' | ')
|
82
|
-
if all_nodes.empty? then [] else driver.find(all_nodes) end
|
83
|
-
end
|
84
|
-
|
85
|
-
protected
|
86
|
-
|
87
|
-
# a reference to the select node if this is an option node
|
88
|
-
def select_node
|
89
|
-
find('./ancestor::select').first
|
90
|
-
end
|
91
|
-
|
92
|
-
def has_value?
|
93
|
-
native.object.hasAttribute('value')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
attr_reader :app, :rack_server, :options
|
98
|
-
|
99
|
-
def initialize(app, options={})
|
100
|
-
@app = app
|
101
|
-
@options = options
|
102
|
-
@rack_server = Capybara::Server.new(@app)
|
103
|
-
@rack_server.boot if Capybara.run_server
|
104
|
-
end
|
105
|
-
|
106
|
-
def visit(path)
|
107
|
-
browser.goto(url(path))
|
108
|
-
end
|
109
|
-
|
110
|
-
def current_url
|
111
|
-
browser.url
|
112
|
-
end
|
113
|
-
|
114
|
-
def source
|
115
|
-
browser.html
|
116
|
-
end
|
117
|
-
|
118
|
-
def body
|
119
|
-
browser.document.as_xml
|
120
|
-
end
|
121
|
-
|
122
|
-
def response_headers
|
123
|
-
browser.response_headers
|
124
|
-
end
|
125
|
-
|
126
|
-
def status_code
|
127
|
-
browser.status_code
|
128
|
-
end
|
129
|
-
|
130
|
-
def find(selector)
|
131
|
-
browser.elements_by_xpath(selector).map { |node| Node.new(self, node) }
|
132
|
-
end
|
133
|
-
|
134
|
-
def wait?; true; end
|
135
|
-
|
136
|
-
def execute_script(script)
|
137
|
-
browser.execute_script script
|
138
|
-
nil
|
139
|
-
end
|
140
|
-
|
141
|
-
def evaluate_script(script)
|
142
|
-
browser.execute_script "#{script}"
|
143
|
-
end
|
144
|
-
|
145
|
-
def browser
|
146
|
-
unless @_browser
|
147
|
-
require 'celerity'
|
148
|
-
@_browser = ::Celerity::Browser.new(options)
|
149
|
-
end
|
150
|
-
|
151
|
-
@_browser
|
152
|
-
end
|
153
|
-
|
154
|
-
def reset!
|
155
|
-
browser.clear_cookies
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
def url(path)
|
161
|
-
rack_server.url(path)
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
@@ -1,26 +0,0 @@
|
|
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, options
|
18
|
-
at_exit do
|
19
|
-
@_browser.close
|
20
|
-
@_browser.exit
|
21
|
-
end
|
22
|
-
end
|
23
|
-
@_browser
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
@@ -1,303 +0,0 @@
|
|
1
|
-
require 'rack/test'
|
2
|
-
require 'rack/utils'
|
3
|
-
require 'mime/types'
|
4
|
-
require 'nokogiri'
|
5
|
-
require 'cgi'
|
6
|
-
|
7
|
-
class Capybara::Driver::RackTest < Capybara::Driver::Base
|
8
|
-
class Node < Capybara::Driver::Node
|
9
|
-
def text
|
10
|
-
native.text
|
11
|
-
end
|
12
|
-
|
13
|
-
def [](name)
|
14
|
-
string_node[name]
|
15
|
-
end
|
16
|
-
|
17
|
-
def value
|
18
|
-
string_node.value
|
19
|
-
end
|
20
|
-
|
21
|
-
def set(value)
|
22
|
-
if tag_name == 'input' and type == 'radio'
|
23
|
-
other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
|
24
|
-
driver.html.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
|
25
|
-
native['checked'] = 'checked'
|
26
|
-
elsif tag_name == 'input' and type == 'checkbox'
|
27
|
-
if value && !native['checked']
|
28
|
-
native['checked'] = 'checked'
|
29
|
-
elsif !value && native['checked']
|
30
|
-
native.remove_attribute('checked')
|
31
|
-
end
|
32
|
-
elsif tag_name == 'input'
|
33
|
-
native['value'] = value.to_s
|
34
|
-
elsif tag_name == "textarea"
|
35
|
-
native.content = value.to_s
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def select_option
|
40
|
-
if select_node['multiple'] != 'multiple'
|
41
|
-
select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
|
42
|
-
end
|
43
|
-
native["selected"] = 'selected'
|
44
|
-
end
|
45
|
-
|
46
|
-
def unselect_option
|
47
|
-
if select_node['multiple'] != 'multiple'
|
48
|
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
49
|
-
end
|
50
|
-
native.remove_attribute('selected')
|
51
|
-
end
|
52
|
-
|
53
|
-
def click
|
54
|
-
if tag_name == 'a'
|
55
|
-
method = self["data-method"] || :get
|
56
|
-
driver.process(method, self[:href].to_s)
|
57
|
-
elsif (tag_name == 'input' and %w(submit image).include?(type)) or
|
58
|
-
((tag_name == 'button') and type.nil? or type == "submit")
|
59
|
-
Form.new(driver, form).submit(self)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def tag_name
|
64
|
-
native.node_name
|
65
|
-
end
|
66
|
-
|
67
|
-
def visible?
|
68
|
-
string_node.visible?
|
69
|
-
end
|
70
|
-
|
71
|
-
def checked?
|
72
|
-
self[:checked]
|
73
|
-
end
|
74
|
-
|
75
|
-
def selected?
|
76
|
-
self[:selected]
|
77
|
-
end
|
78
|
-
|
79
|
-
def path
|
80
|
-
native.path
|
81
|
-
end
|
82
|
-
|
83
|
-
def find(locator)
|
84
|
-
native.xpath(locator).map { |n| self.class.new(driver, n) }
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def string_node
|
90
|
-
@string_node ||= Capybara::Node::Simple.new(native)
|
91
|
-
end
|
92
|
-
|
93
|
-
# a reference to the select node if this is an option node
|
94
|
-
def select_node
|
95
|
-
find('./ancestor::select').first
|
96
|
-
end
|
97
|
-
|
98
|
-
def type
|
99
|
-
native[:type]
|
100
|
-
end
|
101
|
-
|
102
|
-
def form
|
103
|
-
native.ancestors('form').first
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
class Form < Node
|
108
|
-
# This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
|
109
|
-
# the class specifically when determing whether to consturct the request as multipart.
|
110
|
-
# That check should be based solely on the form element's 'enctype' attribute value,
|
111
|
-
# which should probably be provided to Rack::Test in its non-GET request methods.
|
112
|
-
class NilUploadedFile < Rack::Test::UploadedFile
|
113
|
-
def initialize
|
114
|
-
@empty_file = Tempfile.new("nil_uploaded_file")
|
115
|
-
@empty_file.close
|
116
|
-
end
|
117
|
-
|
118
|
-
def original_filename; ""; end
|
119
|
-
def content_type; "application/octet-stream"; end
|
120
|
-
def path; @empty_file.path; end
|
121
|
-
end
|
122
|
-
|
123
|
-
def params(button)
|
124
|
-
params = {}
|
125
|
-
|
126
|
-
native.xpath("(.//input|.//select|.//textarea)[not(@disabled)]").map do |field|
|
127
|
-
case field.name
|
128
|
-
when 'input'
|
129
|
-
if %w(radio checkbox).include? field['type']
|
130
|
-
merge_param!(params, field['name'].to_s, field['value'].to_s) if field['checked']
|
131
|
-
elsif %w(submit image).include? field['type']
|
132
|
-
# TO DO identify the click button here (in document order, rather
|
133
|
-
# than leaving until the end of the params)
|
134
|
-
elsif field['type'] =='file'
|
135
|
-
if multipart?
|
136
|
-
file = \
|
137
|
-
if (value = field['value']).to_s.empty?
|
138
|
-
NilUploadedFile.new
|
139
|
-
else
|
140
|
-
content_type = MIME::Types.type_for(value).first.to_s
|
141
|
-
Rack::Test::UploadedFile.new(value, content_type)
|
142
|
-
end
|
143
|
-
merge_param!(params, field['name'].to_s, file)
|
144
|
-
else
|
145
|
-
merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
|
146
|
-
end
|
147
|
-
else
|
148
|
-
merge_param!(params, field['name'].to_s, field['value'].to_s)
|
149
|
-
end
|
150
|
-
when 'select'
|
151
|
-
if field['multiple'] == 'multiple'
|
152
|
-
options = field.xpath(".//option[@selected]")
|
153
|
-
options.each do |option|
|
154
|
-
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
|
155
|
-
end
|
156
|
-
else
|
157
|
-
option = field.xpath(".//option[@selected]").first
|
158
|
-
option ||= field.xpath('.//option').first
|
159
|
-
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
|
160
|
-
end
|
161
|
-
when 'textarea'
|
162
|
-
merge_param!(params, field['name'].to_s, field.text.to_s)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
merge_param!(params, button[:name], button[:value] || "") if button[:name]
|
166
|
-
params
|
167
|
-
end
|
168
|
-
|
169
|
-
def submit(button)
|
170
|
-
driver.submit(method, native['action'].to_s, params(button))
|
171
|
-
end
|
172
|
-
|
173
|
-
def multipart?
|
174
|
-
self[:enctype] == "multipart/form-data"
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
|
179
|
-
def method
|
180
|
-
self[:method] =~ /post/i ? :post : :get
|
181
|
-
end
|
182
|
-
|
183
|
-
def merge_param!(params, key, value)
|
184
|
-
Rack::Utils.normalize_params(params, key, value)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
include ::Rack::Test::Methods
|
189
|
-
attr_reader :app
|
190
|
-
|
191
|
-
alias_method :response, :last_response
|
192
|
-
alias_method :request, :last_request
|
193
|
-
|
194
|
-
def initialize(app)
|
195
|
-
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
|
196
|
-
@app = app
|
197
|
-
end
|
198
|
-
|
199
|
-
def visit(path, attributes = {})
|
200
|
-
process(:get, path, attributes)
|
201
|
-
end
|
202
|
-
|
203
|
-
def process(method, path, attributes = {})
|
204
|
-
return if path.gsub(/^#{request_path}/, '') =~ /^#/
|
205
|
-
path = request_path + path if path =~ /^\?/
|
206
|
-
send(method, to_binary(path), to_binary( attributes ), env)
|
207
|
-
follow_redirects!
|
208
|
-
end
|
209
|
-
|
210
|
-
def current_url
|
211
|
-
request.url rescue ""
|
212
|
-
end
|
213
|
-
|
214
|
-
def response_headers
|
215
|
-
response.headers
|
216
|
-
end
|
217
|
-
|
218
|
-
def status_code
|
219
|
-
response.status
|
220
|
-
end
|
221
|
-
|
222
|
-
def to_binary(object)
|
223
|
-
return object unless Kernel.const_defined?(:Encoding)
|
224
|
-
|
225
|
-
if object.respond_to?(:force_encoding)
|
226
|
-
object.dup.force_encoding(Encoding::ASCII_8BIT)
|
227
|
-
elsif object.respond_to?(:each_pair) #Hash
|
228
|
-
{}.tap { |x| object.each_pair {|k,v| x[to_binary(k)] = to_binary(v) } }
|
229
|
-
elsif object.respond_to?(:each) #Array
|
230
|
-
object.map{|x| to_binary(x)}
|
231
|
-
else
|
232
|
-
object
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def submit(method, path, attributes)
|
237
|
-
path = request_path if not path or path.empty?
|
238
|
-
send(method, to_binary(path), to_binary(attributes), env)
|
239
|
-
follow_redirects!
|
240
|
-
end
|
241
|
-
|
242
|
-
def find(selector)
|
243
|
-
html.xpath(selector).map { |node| Node.new(self, node) }
|
244
|
-
end
|
245
|
-
|
246
|
-
def body
|
247
|
-
@body ||= response.body
|
248
|
-
end
|
249
|
-
|
250
|
-
def html
|
251
|
-
@html ||= Nokogiri::HTML(body)
|
252
|
-
end
|
253
|
-
alias_method :source, :body
|
254
|
-
|
255
|
-
def reset!
|
256
|
-
clear_rack_mock_session
|
257
|
-
end
|
258
|
-
|
259
|
-
def get(*args, &block); reset_cache; super; end
|
260
|
-
def post(*args, &block); reset_cache; super; end
|
261
|
-
def put(*args, &block); reset_cache; super; end
|
262
|
-
def delete(*args, &block); reset_cache; super; end
|
263
|
-
|
264
|
-
def follow_redirects!
|
265
|
-
5.times do
|
266
|
-
follow_redirect! if response.redirect?
|
267
|
-
end
|
268
|
-
raise Capybara::InfiniteRedirectError, "redirected more than 5 times, check for infinite redirects." if response.redirect?
|
269
|
-
end
|
270
|
-
|
271
|
-
private
|
272
|
-
|
273
|
-
def reset_cache
|
274
|
-
@body = nil
|
275
|
-
@html = nil
|
276
|
-
end
|
277
|
-
|
278
|
-
def build_rack_mock_session # :nodoc:
|
279
|
-
Rack::MockSession.new(app, Capybara.default_host || "www.example.com")
|
280
|
-
end
|
281
|
-
|
282
|
-
# Rack::Test::Methods does not provide methods for manipulating the session
|
283
|
-
# list so these must be manipulated directly.
|
284
|
-
def clear_rack_mock_session
|
285
|
-
@_rack_test_sessions = nil
|
286
|
-
@_rack_mock_sessions = nil
|
287
|
-
end
|
288
|
-
|
289
|
-
def request_path
|
290
|
-
request.path rescue ""
|
291
|
-
end
|
292
|
-
|
293
|
-
def env
|
294
|
-
env = {}
|
295
|
-
begin
|
296
|
-
env["HTTP_REFERER"] = request.url
|
297
|
-
rescue Rack::Test::Error
|
298
|
-
# no request yet
|
299
|
-
end
|
300
|
-
env
|
301
|
-
end
|
302
|
-
|
303
|
-
end
|