capybara 0.4.1.2 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/History.txt +46 -0
  2. data/README.rdoc +211 -64
  3. data/lib/capybara.rb +31 -15
  4. data/lib/capybara/cucumber.rb +12 -16
  5. data/lib/capybara/dsl.rb +65 -28
  6. data/lib/capybara/node/actions.rb +7 -5
  7. data/lib/capybara/node/document.rb +8 -0
  8. data/lib/capybara/node/finders.rb +11 -7
  9. data/lib/capybara/node/matchers.rb +32 -6
  10. data/lib/capybara/node/simple.rb +20 -0
  11. data/lib/capybara/rack_test/browser.rb +115 -0
  12. data/lib/capybara/rack_test/driver.rb +77 -0
  13. data/lib/capybara/rack_test/form.rb +80 -0
  14. data/lib/capybara/rack_test/node.rb +101 -0
  15. data/lib/capybara/rspec.rb +11 -3
  16. data/lib/capybara/rspec/features.rb +22 -0
  17. data/lib/capybara/rspec/matchers.rb +146 -0
  18. data/lib/capybara/selector.rb +27 -8
  19. data/lib/capybara/selenium/driver.rb +148 -0
  20. data/lib/capybara/selenium/node.rb +91 -0
  21. data/lib/capybara/session.rb +42 -15
  22. data/lib/capybara/spec/driver.rb +55 -1
  23. data/lib/capybara/spec/fixtures/capybara.jpg +0 -0
  24. data/lib/capybara/spec/public/test.js +7 -2
  25. data/lib/capybara/spec/session.rb +51 -7
  26. data/lib/capybara/spec/session/attach_file_spec.rb +9 -6
  27. data/lib/capybara/spec/session/click_button_spec.rb +35 -0
  28. data/lib/capybara/spec/session/current_host_spec.rb +62 -0
  29. data/lib/capybara/spec/session/fill_in_spec.rb +6 -0
  30. data/lib/capybara/spec/session/find_spec.rb +23 -1
  31. data/lib/capybara/spec/session/first_spec.rb +39 -6
  32. data/lib/capybara/spec/session/has_css_spec.rb +30 -0
  33. data/lib/capybara/spec/session/has_field_spec.rb +47 -11
  34. data/lib/capybara/spec/session/javascript.rb +0 -1
  35. data/lib/capybara/spec/session/text_spec.rb +19 -0
  36. data/lib/capybara/spec/test_app.rb +9 -0
  37. data/lib/capybara/spec/views/form.erb +8 -3
  38. data/lib/capybara/spec/views/header_links.erb +7 -0
  39. data/lib/capybara/spec/views/host_links.erb +12 -0
  40. data/lib/capybara/spec/views/with_html.erb +6 -2
  41. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  42. data/lib/capybara/spec/views/with_js.erb +4 -0
  43. data/lib/capybara/util/save_and_open_page.rb +7 -3
  44. data/lib/capybara/util/timeout.rb +2 -2
  45. data/lib/capybara/version.rb +1 -1
  46. data/spec/capybara_spec.rb +1 -1
  47. data/spec/driver/rack_test_driver_spec.rb +24 -2
  48. data/spec/driver/selenium_driver_spec.rb +2 -1
  49. data/spec/dsl_spec.rb +56 -4
  50. data/spec/rspec/features_spec.rb +45 -0
  51. data/spec/rspec/matchers_spec.rb +451 -0
  52. data/spec/rspec_spec.rb +9 -2
  53. data/spec/save_and_open_page_spec.rb +9 -13
  54. data/spec/server_spec.rb +4 -0
  55. data/spec/session/rack_test_session_spec.rb +2 -2
  56. data/spec/session/selenium_session_spec.rb +1 -1
  57. data/spec/spec_helper.rb +0 -14
  58. data/spec/string_spec.rb +1 -1
  59. metadata +60 -69
  60. data/lib/capybara/driver/celerity_driver.rb +0 -164
  61. data/lib/capybara/driver/culerity_driver.rb +0 -26
  62. data/lib/capybara/driver/rack_test_driver.rb +0 -303
  63. data/lib/capybara/driver/selenium_driver.rb +0 -161
  64. data/spec/driver/celerity_driver_spec.rb +0 -13
  65. data/spec/driver/culerity_driver_spec.rb +0 -14
  66. data/spec/driver/remote_culerity_driver_spec.rb +0 -22
  67. data/spec/driver/remote_selenium_driver_spec.rb +0 -16
  68. data/spec/session/celerity_session_spec.rb +0 -24
  69. 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