webrat 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/History.txt +76 -14
  2. data/README.txt +40 -36
  3. data/Rakefile +80 -18
  4. data/TODO.txt +9 -3
  5. data/init.rb +1 -1
  6. data/lib/webrat.rb +30 -5
  7. data/lib/webrat/core.rb +12 -0
  8. data/lib/webrat/core/area.rb +44 -0
  9. data/lib/webrat/core/field.rb +332 -0
  10. data/lib/webrat/core/flunk.rb +7 -0
  11. data/lib/webrat/core/form.rb +130 -0
  12. data/lib/webrat/core/label.rb +18 -0
  13. data/lib/webrat/core/link.rb +101 -0
  14. data/lib/webrat/core/locators.rb +92 -0
  15. data/lib/webrat/core/logging.rb +25 -0
  16. data/lib/webrat/core/matchers.rb +4 -0
  17. data/lib/webrat/core/matchers/have_content.rb +94 -0
  18. data/lib/webrat/core/matchers/have_selector.rb +39 -0
  19. data/lib/webrat/core/matchers/have_tag.rb +58 -0
  20. data/lib/webrat/core/matchers/have_xpath.rb +85 -0
  21. data/lib/webrat/core/methods.rb +44 -0
  22. data/lib/webrat/core/mime.rb +29 -0
  23. data/lib/webrat/core/nokogiri.rb +42 -0
  24. data/lib/webrat/core/scope.rb +208 -0
  25. data/lib/webrat/core/select_option.rb +29 -0
  26. data/lib/webrat/core/session.rb +188 -0
  27. data/lib/webrat/core_extensions/blank.rb +58 -0
  28. data/lib/webrat/core_extensions/deprecate.rb +8 -0
  29. data/lib/webrat/core_extensions/detect_mapped.rb +12 -0
  30. data/lib/webrat/core_extensions/hash_with_indifferent_access.rb +131 -0
  31. data/lib/webrat/core_extensions/meta_class.rb +6 -0
  32. data/lib/webrat/core_extensions/nil_to_param.rb +5 -0
  33. data/lib/webrat/mechanize.rb +28 -0
  34. data/lib/webrat/merb.rb +75 -0
  35. data/lib/webrat/rack.rb +24 -0
  36. data/lib/webrat/rails.rb +102 -0
  37. data/lib/webrat/rails/redirect_actions.rb +18 -0
  38. data/lib/webrat/selenium.rb +3 -0
  39. data/lib/webrat/selenium/location_strategy_javascript/button.js +12 -0
  40. data/lib/webrat/selenium/location_strategy_javascript/label.js +16 -0
  41. data/lib/webrat/selenium/location_strategy_javascript/webrat.js +5 -0
  42. data/lib/webrat/selenium/location_strategy_javascript/webratlink.js +9 -0
  43. data/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
  44. data/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
  45. data/lib/webrat/selenium/selenium_extensions.js +6 -0
  46. data/lib/webrat/selenium/selenium_session.rb +137 -0
  47. data/lib/webrat/sinatra.rb +19 -0
  48. metadata +66 -52
  49. data/Manifest.txt +0 -20
  50. data/lib/webrat/rails_extensions.rb +0 -27
  51. data/lib/webrat/session.rb +0 -523
  52. data/test/checks_test.rb +0 -121
  53. data/test/chooses_test.rb +0 -74
  54. data/test/clicks_button_test.rb +0 -308
  55. data/test/clicks_link_test.rb +0 -193
  56. data/test/fills_in_test.rb +0 -139
  57. data/test/helper.rb +0 -21
  58. data/test/reloads_test.rb +0 -26
  59. data/test/selects_test.rb +0 -93
  60. data/test/visits_test.rb +0 -31
@@ -1,27 +0,0 @@
1
- module ActionController
2
- module Integration
3
-
4
- class Session
5
-
6
- unless instance_methods.include?("put_via_redirect")
7
- # Waiting for http://dev.rubyonrails.org/ticket/10497 to be committed.
8
- def put_via_redirect(path, parameters = {}, headers = {})
9
- put path, parameters, headers
10
- follow_redirect! while redirect?
11
- status
12
- end
13
- end
14
-
15
- unless instance_methods.include?("delete_via_redirect")
16
- # Waiting for http://dev.rubyonrails.org/ticket/10497 to be committed.
17
- def delete_via_redirect(path, parameters = {}, headers = {})
18
- delete path, parameters, headers
19
- follow_redirect! while redirect?
20
- status
21
- end
22
- end
23
-
24
- end
25
-
26
- end
27
- end
@@ -1,523 +0,0 @@
1
- require "hpricot"
2
- require "English"
3
-
4
- module ActionController
5
- module Integration
6
-
7
- class Session
8
- # Issues a GET request for a page, follows any redirects, and verifies the final page
9
- # load was successful.
10
- #
11
- # Example:
12
- # visits "/"
13
- def visits(path)
14
- request_page(:get, path)
15
- end
16
-
17
- # Issues a request for the URL pointed to by a link on the current page,
18
- # follows any redirects, and verifies the final page load was successful.
19
- #
20
- # clicks_link has very basic support for detecting Rails-generated
21
- # JavaScript onclick handlers for PUT, POST and DELETE links, as well as
22
- # CSRF authenticity tokens if they are present.
23
- #
24
- # Example:
25
- # clicks_link "Sign up"
26
- def clicks_link(link_text)
27
- clicks_one_link_of(all_links, link_text)
28
- end
29
-
30
- # Works like clicks_link, but only looks for the link text within a given selector
31
- #
32
- # Example:
33
- # clicks_link_within "#user_12", "Vote"
34
- def clicks_link_within(selector, link_text)
35
- clicks_one_link_of(links_within(selector), link_text)
36
- end
37
-
38
- # Works like clicks_link, but forces a GET request
39
- #
40
- # Example:
41
- # clicks_get_link "Log out"
42
- def clicks_get_link(link_text)
43
- clicks_link_with_method(link_text, :get)
44
- end
45
-
46
- # Works like clicks_link, but issues a DELETE request instead of a GET
47
- #
48
- # Example:
49
- # clicks_delete_link "Log out"
50
- def clicks_delete_link(link_text)
51
- clicks_link_with_method(link_text, :delete)
52
- end
53
-
54
- # Works like clicks_link, but issues a POST request instead of a GET
55
- #
56
- # Example:
57
- # clicks_post_link "Vote"
58
- def clicks_post_link(link_text)
59
- clicks_link_with_method(link_text, :post)
60
- end
61
-
62
- # Works like clicks_link, but issues a PUT request instead of a GET
63
- #
64
- # Example:
65
- # clicks_put_link "Update profile"
66
- def clicks_put_link(link_text)
67
- clicks_link_with_method(link_text, :put)
68
- end
69
-
70
- # Verifies an input field or textarea exists on the current page, and stores a value for
71
- # it which will be sent when the form is submitted.
72
- #
73
- # Examples:
74
- # fills_in "Email", :with => "user@example.com"
75
- # fills_in "user[email]", :with => "user@example.com"
76
- #
77
- # The field value is required, and must be specified in <tt>options[:with]</tt>.
78
- # <tt>field</tt> can be either the value of a name attribute (i.e. <tt>user[email]</tt>)
79
- # or the text inside a <tt><label></tt> element that points at the <tt><input></tt> field.
80
- def fills_in(field, options = {})
81
- value = options[:with]
82
- return flunk("No value was provided") if value.nil?
83
- input = find_field_by_name_or_label(field)
84
- return flunk("Could not find input #{field.inspect}") if input.nil?
85
- add_form_data(input, value)
86
- # TODO - Set current form
87
- end
88
-
89
- # Verifies that a an option element exists on the current page with the specified
90
- # text. You can optionally restrict the search to a specific select list by
91
- # assigning <tt>options[:from]</tt> the value of the select list's name or
92
- # a label. Stores the option's value to be sent when the form is submitted.
93
- #
94
- # Examples:
95
- # selects "January"
96
- # selects "February", :from => "event_month"
97
- # selects "February", :from => "Event Month"
98
- def selects(option_text, options = {})
99
- if options[:from]
100
- select = find_select_list_by_name_or_label(options[:from])
101
- return flunk("Could not find select list #{options[:from].inspect}") if select.nil?
102
- option_node = find_option_by_value(option_text, select)
103
- return flunk("Could not find option #{option_text.inspect}") if option_node.nil?
104
- else
105
- option_node = find_option_by_value(option_text)
106
- return flunk("Could not find option #{option_text.inspect}") if option_node.nil?
107
- select = option_node.parent
108
- end
109
- add_form_data(select, option_node.attributes["value"] || option_node.innerHTML)
110
- # TODO - Set current form
111
- end
112
-
113
- # Verifies that an input checkbox exists on the current page and marks it
114
- # as checked, so that the value will be submitted with the form.
115
- #
116
- # Example:
117
- # checks 'Remember Me'
118
- def checks(field)
119
- checkbox = find_field_by_name_or_label(field)
120
- return flunk("Could not find checkbox #{field.inspect}") if checkbox.nil?
121
- return flunk("Input #{checkbox.inspect} is not a checkbox") unless checkbox.attributes['type'] == 'checkbox'
122
- add_form_data(checkbox, checkbox.attributes["value"] || "on")
123
- end
124
-
125
- # Verifies that an input checkbox exists on the current page and marks it
126
- # as unchecked, so that the value will not be submitted with the form.
127
- #
128
- # Example:
129
- # unchecks 'Remember Me'
130
- def unchecks(field)
131
- checkbox = find_field_by_name_or_label(field)
132
- return flunk("Could not find checkbox #{field.inspect}") if checkbox.nil?
133
- return flunk("Input #{checkbox.inspect} is not a checkbox") unless checkbox.attributes['type'] == 'checkbox'
134
- remove_form_data(checkbox)
135
-
136
- (form_for_node(checkbox) / "input").each do |input|
137
- next unless input.attributes["type"] == "hidden" && input.attributes["name"] == checkbox.attributes["name"]
138
- add_form_data(input, input.attributes["value"])
139
- end
140
- end
141
-
142
- # Verifies that an input radio button exists on the current page and marks it
143
- # as checked, so that the value will be submitted with the form.
144
- #
145
- # Example:
146
- # chooses 'First Option'
147
- def chooses(field)
148
- radio = find_field_by_name_or_label(field)
149
- return flunk("Could not find radio button #{field.inspect}") if radio.nil?
150
- return flunk("Input #{radio.inspect} is not a radio button") unless radio.attributes['type'] == 'radio'
151
- add_form_data(radio, radio.attributes["value"] || "on")
152
- end
153
-
154
- # Verifies that a submit button exists for the form, then submits the form, follows
155
- # any redirects, and verifies the final page was successful.
156
- #
157
- # Example:
158
- # clicks_button "Login"
159
- # clicks_button
160
- #
161
- # The URL and HTTP method for the form submission are automatically read from the
162
- # <tt>action</tt> and <tt>method</tt> attributes of the <tt><form></tt> element.
163
- def clicks_button(value = nil)
164
- button = value ? find_button(value) : submit_buttons.first
165
- return flunk("Could not find button #{value.inspect}") if button.nil?
166
- add_form_data(button, button.attributes["value"]) unless button.attributes["name"].blank?
167
- submit_form(form_for_node(button))
168
- end
169
-
170
- def submits_form(form_id = nil) # :nodoc:
171
- end
172
-
173
- # Saves the currently loaded page out to RAILS_ROOT/tmp/ and opens it in the default
174
- # web browser if on OS X. Useful for debugging.
175
- #
176
- # Example:
177
- # save_and_open_page
178
- def save_and_open_page
179
- return unless File.exist?(RAILS_ROOT + "/tmp")
180
-
181
- filename = "webrat-#{Time.now.to_i}.html"
182
- File.open(RAILS_ROOT + "/tmp/#{filename}", "w") do |f|
183
- f.write response.body
184
- end
185
- `open tmp/#{filename}`
186
- end
187
-
188
- # Reloads the last page requested. Note that this will resubmit forms
189
- # and their data.
190
- #
191
- # Example:
192
- # reloads
193
- def reloads
194
- request_page(*@last_request_args) if defined?(@last_request_args) && @last_request_args
195
- end
196
-
197
- protected # Methods you could call, but probably shouldn't
198
-
199
- def authenticity_token_value(onclick)
200
- return unless onclick && onclick.include?("s.setAttribute('name', 'authenticity_token');") &&
201
- onclick =~ /s\.setAttribute\('value', '([a-f0-9]{40})'\);/
202
- $LAST_MATCH_INFO.captures.first
203
- end
204
-
205
- def http_method_from_js(onclick)
206
- if !onclick.blank? && onclick.include?("f.submit()")
207
- http_method_from_js_form(onclick)
208
- else
209
- :get
210
- end
211
- end
212
-
213
- def http_method_from_js_form(onclick)
214
- if onclick.include?("m.setAttribute('name', '_method')")
215
- http_method_from_fake_method_param(onclick)
216
- else
217
- :post
218
- end
219
- end
220
-
221
- def http_method_from_fake_method_param(onclick)
222
- if onclick.include?("m.setAttribute('value', 'delete')")
223
- :delete
224
- elsif onclick.include?("m.setAttribute('value', 'put')")
225
- :put
226
- else
227
- raise "No HTTP method for _method param in #{onclick.inspect}"
228
- end
229
- end
230
-
231
- def clicks_link_with_method(link_text, http_method) # :nodoc:
232
- link = all_links.detect { |el| el.innerHTML =~ /#{link_text}/i }
233
- return flunk("No link with text #{link_text.inspect} was found") if link.nil?
234
- request_page(http_method, link.attributes["href"])
235
- end
236
-
237
- def find_shortest_matching_link(links, link_text)
238
- candidates = links.select { |el| el.innerHTML =~ /#{link_text}/i }
239
- candidates.sort_by { |el| el.innerText.strip.size }.first
240
- end
241
-
242
- def clicks_one_link_of(links, link_text)
243
- link = find_shortest_matching_link(links, link_text)
244
-
245
- return flunk("No link with text #{link_text.inspect} was found") if link.nil?
246
-
247
- onclick = link.attributes["onclick"]
248
- href = link.attributes["href"]
249
-
250
- http_method = http_method_from_js(onclick)
251
- authenticity_token = authenticity_token_value(onclick)
252
-
253
- request_page(http_method, href, authenticity_token.blank? ? {} : {"authenticity_token" => authenticity_token}) unless href =~ /^#/ && http_method == :get
254
- end
255
-
256
- def find_field_by_name_or_label(name_or_label) # :nodoc:
257
- input = find_field_by_name(name_or_label)
258
- return input if input
259
-
260
- label = find_form_label(name_or_label)
261
- label ? input_for_label(label) : nil
262
- end
263
-
264
- def find_select_list_by_name_or_label(name_or_label) # :nodoc:
265
- select = find_select_list_by_name(name_or_label)
266
- return select if select
267
-
268
- label = find_form_label(name_or_label)
269
- label ? select_list_for_label(label) : nil
270
- end
271
-
272
- def find_option_by_value(option_value, select=nil) # :nodoc:
273
- options = select.nil? ? option_nodes : (select / "option")
274
- options.detect { |el| el.innerHTML == option_value }
275
- end
276
-
277
- def find_button(value = nil) # :nodoc:
278
- return nil unless value
279
- submit_buttons.detect { |el| el.attributes["value"] =~ /^\W*#{value}\b/i }
280
- end
281
-
282
- def add_form_data(input_element, value) # :nodoc:
283
- form = form_for_node(input_element)
284
- data = param_parser.parse_query_parameters("#{input_element.attributes["name"]}=#{value}")
285
- merge_form_data(form_number(form), data)
286
- end
287
-
288
- def remove_form_data(input_element) # :nodoc:
289
- form = form_for_node(input_element)
290
- form_number = form_number(form)
291
- form_data[form_number] ||= {}
292
- form_data[form_number].delete(input_element.attributes['name'])
293
- end
294
-
295
- def submit_form(form) # :nodoc:
296
- form_number = form_number(form)
297
- request_page(form_method(form), form_action(form), form_data[form_number])
298
- end
299
-
300
- def merge_form_data(form_number, data) # :nodoc:
301
- form_data[form_number] ||= {}
302
-
303
- data.each do |key, value|
304
- case form_data[form_number][key]
305
- when Hash, HashWithIndifferentAccess; then merge(form_data[form_number][key], value)
306
- when Array; then form_data[form_number][key] += value
307
- else form_data[form_number][key] = value
308
- end
309
- end
310
- end
311
-
312
- def merge(a, b) # :nodoc:
313
- a.keys.each do |k|
314
- if b.has_key?(k)
315
- case [a[k], b[k]].map(&:class)
316
- when [Hash, Hash]
317
- a[k] = merge(a[k], b[k])
318
- b.delete(k)
319
- when [Array, Array]
320
- a[k] += b[k]
321
- b.delete(k)
322
- end
323
- end
324
- end
325
- a.merge!(b)
326
- end
327
-
328
- def request_page(method, url, data = {}) # :nodoc:
329
- debug_log "REQUESTING PAGE: #{method.to_s.upcase} #{url} with #{data.inspect}"
330
- @current_url = url
331
- @last_request_args = [method, url, data]
332
- self.send "#{method}_via_redirect", @current_url, data || {}
333
-
334
- if response.body =~ /Exception caught/ || response.body.blank?
335
- save_and_open_page
336
- end
337
-
338
- assert_response :success
339
- reset_dom
340
- end
341
-
342
- def input_for_label(label) # :nodoc:
343
- if input = (label / "input").first
344
- input # nested inputs within labels
345
- else
346
- # input somewhere else, referenced by id
347
- input_id = label.attributes["for"]
348
- (dom / "##{input_id}").first
349
- end
350
- end
351
-
352
- def select_list_for_label(label) # :nodoc:
353
- if select_list = (label / "select").first
354
- select_list # nested inputs within labels
355
- else
356
- # input somewhere else, referenced by id
357
- select_list_id = label.attributes["for"]
358
- (dom / "##{select_list_id}").first
359
- end
360
- end
361
-
362
- def param_parser # :nodoc:
363
- if defined?(CGIMethods)
364
- CGIMethods
365
- else
366
- ActionController::AbstractRequest
367
- end
368
- end
369
-
370
- def submit_buttons # :nodoc:
371
- input_fields.select { |el| el.attributes["type"] == "submit" }
372
- end
373
-
374
- def find_field_by_name(name) # :nodoc:
375
- find_input_by_name(name) || find_textarea_by_name(name)
376
- end
377
-
378
- def find_input_by_name(name) # :nodoc:
379
- input_fields.detect { |el| el.attributes["name"] == name }
380
- end
381
-
382
- def find_select_list_by_name(name) # :nodoc:
383
- select_lists.detect { |el| el.attributes["name"] == name }
384
- end
385
-
386
- def find_textarea_by_name(name) # :nodoc:
387
- textarea_fields.detect{ |el| el.attributes['name'] == name }
388
- end
389
-
390
- def find_form_label(text) # :nodoc:
391
- candidates = form_labels.select { |el| el.innerText =~ /^\W*#{text}\b/i }
392
- candidates.sort_by { |el| el.innerText.strip.size }.first
393
- end
394
-
395
- def form_action(form) # :nodoc:
396
- form.attributes["action"].blank? ? current_url : form.attributes["action"]
397
- end
398
-
399
- def form_method(form) # :nodoc:
400
- form.attributes["method"].blank? ? :get : form.attributes["method"].downcase
401
- end
402
-
403
- def add_default_params # :nodoc:
404
- (dom / "form").each do |form|
405
- add_default_params_for(form)
406
- end
407
- end
408
-
409
- def add_default_params_for(form) # :nodoc:
410
- add_default_params_from_inputs_for(form)
411
- add_default_params_from_checkboxes_for(form)
412
- add_default_params_from_radio_buttons_for(form)
413
- add_default_params_from_textareas_for(form)
414
- add_default_params_from_selects_for(form)
415
- end
416
-
417
- def add_default_params_from_inputs_for(form) # :nodoc:
418
- (form / "input").each do |input|
419
- next unless %w[text hidden].include?(input.attributes["type"])
420
- add_form_data(input, input.attributes["value"])
421
- end
422
- end
423
-
424
- def add_default_params_from_checkboxes_for(form) # :nodoc:
425
- (form / "input[@type='checkbox][@checked='checked']").each do |input|
426
- add_form_data(input, input.attributes["value"] || "on")
427
- end
428
- end
429
-
430
- def add_default_params_from_radio_buttons_for(form) # :nodoc:
431
- (form / "input[@type='radio][@checked='checked']").each do |input|
432
- add_form_data(input, input.attributes["value"])
433
- end
434
- end
435
-
436
- def add_default_params_from_textareas_for(form) # :nodoc:
437
- (form / "textarea").each do |input|
438
- add_form_data(input, input.inner_html)
439
- end
440
- end
441
-
442
- def add_default_params_from_selects_for(form) # :nodoc:
443
- (form / "select").each do |select|
444
- selected_options = select / "option[@selected='selected']"
445
- selected_options = select / "option:first" if selected_options.empty?
446
- selected_options.each do |option|
447
- add_form_data(select, option.attributes["value"] || option.innerHTML)
448
- end
449
- end
450
- end
451
-
452
- def form_for_node(node) # :nodoc:
453
- return node if node.name == "form"
454
- node = node.parent until node.name == "form"
455
- node
456
- end
457
-
458
- def reset_dom # :nodoc:
459
- @form_data = []
460
- @dom = nil
461
- end
462
-
463
- def form_data # :nodoc:
464
- @form_data ||= []
465
- end
466
-
467
- def all_links # :nodoc:
468
- (dom / "a[@href]")
469
- end
470
-
471
- def links_within(selector) # :nodoc:
472
- (dom / selector / "a[@href]")
473
- end
474
-
475
- def form_number(form) # :nodoc:
476
- (dom / "form").index(form)
477
- end
478
-
479
- def input_fields # :nodoc:
480
- (dom / "input")
481
- end
482
-
483
- def textarea_fields # :nodoc
484
- (dom / "textarea")
485
- end
486
-
487
- def form_labels # :nodoc:
488
- (dom / "label")
489
- end
490
-
491
- def select_lists # :nodoc:
492
- (dom / "select")
493
- end
494
-
495
- def option_nodes # :nodoc:
496
- (dom / "option")
497
- end
498
-
499
- def dom # :nodoc:
500
- return @dom if defined?(@dom) && @dom
501
- raise "You must visit a path before working with the page." unless response
502
- @dom = Hpricot(response.body)
503
- add_default_params
504
- @dom
505
- end
506
-
507
- def debug_log(message) # :nodoc:
508
- return unless logger
509
- logger.debug
510
- end
511
-
512
- def logger # :nodoc:
513
- if defined? RAILS_DEFAULT_LOGGER
514
- RAILS_DEFAULT_LOGGER
515
- else
516
- nil
517
- end
518
- end
519
-
520
- end
521
-
522
- end
523
- end