webrat 0.2.0 → 0.3.0

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.
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
@@ -0,0 +1,332 @@
1
+ require "cgi"
2
+ require "webrat/core_extensions/blank"
3
+ require "webrat/core_extensions/nil_to_param"
4
+
5
+ module Webrat
6
+ class Field #:nodoc:
7
+
8
+ def self.class_for_element(element)
9
+ if element.name == "input"
10
+ if %w[submit image].include?(element["type"])
11
+ field_class = "button"
12
+ else
13
+ field_class = element["type"] || "text" #default type; 'type' attribute is not mandatory
14
+ end
15
+ else
16
+ field_class = element.name
17
+ end
18
+ Webrat.const_get("#{field_class.capitalize}Field")
19
+ rescue NameError
20
+ raise "Invalid field element: #{element.inspect}"
21
+ end
22
+
23
+ def initialize(form, element)
24
+ @form = form
25
+ @element = element
26
+
27
+ @value = default_value
28
+ end
29
+
30
+ def label_text
31
+ return nil if labels.empty?
32
+ labels.first.text
33
+ end
34
+
35
+ def matches_id?(id)
36
+ @element["id"] == id.to_s
37
+ end
38
+
39
+ def matches_name?(name)
40
+ @element["name"] == name.to_s
41
+ end
42
+
43
+ def matches_label?(label_text)
44
+ return false if labels.empty?
45
+ labels.any? { |label| label.matches_text?(label_text) }
46
+ end
47
+
48
+ def matches_alt?(alt)
49
+ @element["alt"] =~ /^\W*#{Regexp.escape(alt.to_s)}/i
50
+ end
51
+
52
+ def disabled?
53
+ @element.attributes.has_key?("disabled") && @element["disabled"] != 'false'
54
+ end
55
+
56
+ def raise_error_if_disabled
57
+ raise "Cannot interact with disabled form element (#{self})" if disabled?
58
+ end
59
+
60
+ def to_param
61
+ return nil if disabled?
62
+
63
+ key_and_value = "#{name}=#{escaped_value}"
64
+
65
+ if defined?(CGIMethods)
66
+ CGIMethods.parse_query_parameters(key_and_value)
67
+ elsif defined?(ActionController::AbstractRequest)
68
+ ActionController::AbstractRequest.parse_query_parameters(key_and_value)
69
+ else
70
+ ::Merb::Parse.query(key_and_value)
71
+ end
72
+ end
73
+
74
+ def set(value)
75
+ @value = value
76
+ end
77
+
78
+ def unset
79
+ @value = default_value
80
+ end
81
+
82
+ protected
83
+
84
+ def id
85
+ @element["id"]
86
+ end
87
+
88
+ def name
89
+ @element["name"]
90
+ end
91
+
92
+ def escaped_value
93
+ CGI.escape(@value.to_s)
94
+ end
95
+
96
+ def labels
97
+ @labels ||= label_elements.map { |element| Label.new(self, element) }
98
+ end
99
+
100
+ def label_elements
101
+ return @label_elements unless @label_elements.nil?
102
+ @label_elements = []
103
+
104
+ parent = @element.parent
105
+ while parent.respond_to?(:parent)
106
+ if parent.name == 'label'
107
+ @label_elements.push parent
108
+ break
109
+ end
110
+ parent = parent.parent
111
+ end
112
+
113
+ unless id.blank?
114
+ @label_elements += @form.element.search("label[@for='#{id}']")
115
+ end
116
+
117
+ @label_elements
118
+ end
119
+
120
+ def default_value
121
+ @element["value"]
122
+ end
123
+
124
+ def replace_param_value(params, oval, nval)
125
+ output = Hash.new
126
+ params.each do |key, value|
127
+ case value
128
+ when Hash
129
+ value = replace_param_value(value, oval, nval)
130
+ when Array
131
+ value = value.map { |o| o == oval ? nval : oval }
132
+ when oval
133
+ value = nval
134
+ end
135
+ output[key] = value
136
+ end
137
+ output
138
+ end
139
+ end
140
+
141
+ class ButtonField < Field #:nodoc:
142
+
143
+ def matches_text?(text)
144
+ @element.inner_html =~ /#{Regexp.escape(text.to_s)}/i
145
+ end
146
+
147
+ def matches_value?(value)
148
+ @element["value"] =~ /^\W*#{Regexp.escape(value.to_s)}/i || matches_text?(value) || matches_alt?(value)
149
+ end
150
+
151
+ def to_param
152
+ return nil if @value.nil?
153
+ super
154
+ end
155
+
156
+ def default_value
157
+ nil
158
+ end
159
+
160
+ def click
161
+ raise_error_if_disabled
162
+ set(@element["value"]) unless @element["name"].blank?
163
+ @form.submit
164
+ end
165
+
166
+ end
167
+
168
+ class HiddenField < Field #:nodoc:
169
+
170
+ def to_param
171
+ if collection_name?
172
+ super
173
+ else
174
+ checkbox_with_same_name = @form.field(name, CheckboxField)
175
+
176
+ if checkbox_with_same_name.to_param.blank?
177
+ super
178
+ else
179
+ nil
180
+ end
181
+ end
182
+ end
183
+
184
+ protected
185
+
186
+ def collection_name?
187
+ name =~ /\[\]/
188
+ end
189
+
190
+ end
191
+
192
+ class CheckboxField < Field #:nodoc:
193
+
194
+ def to_param
195
+ return nil if @value.nil?
196
+ super
197
+ end
198
+
199
+ def check
200
+ raise_error_if_disabled
201
+ set(@element["value"] || "on")
202
+ end
203
+
204
+ def checked?
205
+ @element["checked"] == "checked"
206
+ end
207
+
208
+ def uncheck
209
+ raise_error_if_disabled
210
+ set(nil)
211
+ end
212
+
213
+ protected
214
+
215
+ def default_value
216
+ if @element["checked"] == "checked"
217
+ @element["value"] || "on"
218
+ else
219
+ nil
220
+ end
221
+ end
222
+
223
+ end
224
+
225
+ class PasswordField < Field #:nodoc:
226
+ end
227
+
228
+ class RadioField < Field #:nodoc:
229
+
230
+ def to_param
231
+ return nil if @value.nil?
232
+ super
233
+ end
234
+
235
+ def choose
236
+ raise_error_if_disabled
237
+ other_options.each do |option|
238
+ option.set(nil)
239
+ end
240
+
241
+ set(@element["value"] || "on")
242
+ end
243
+
244
+ protected
245
+
246
+ def other_options
247
+ @form.fields.select { |f| f.name == name }
248
+ end
249
+
250
+ def default_value
251
+ if @element["checked"] == "checked"
252
+ @element["value"] || "on"
253
+ else
254
+ nil
255
+ end
256
+ end
257
+
258
+ end
259
+
260
+ class TextareaField < Field #:nodoc:
261
+
262
+ protected
263
+
264
+ def default_value
265
+ @element.inner_html
266
+ end
267
+
268
+ end
269
+
270
+ class FileField < Field #:nodoc:
271
+
272
+ attr_accessor :content_type
273
+
274
+ def set(value, content_type = nil)
275
+ super(value)
276
+ @content_type = content_type
277
+ end
278
+
279
+ def to_param
280
+ if @value.nil?
281
+ super
282
+ else
283
+ replace_param_value(super, @value, test_uploaded_file)
284
+ end
285
+ end
286
+
287
+ protected
288
+
289
+ def test_uploaded_file
290
+ if content_type
291
+ ActionController::TestUploadedFile.new(@value, content_type)
292
+ else
293
+ ActionController::TestUploadedFile.new(@value)
294
+ end
295
+ end
296
+
297
+ end
298
+
299
+ class TextField < Field #:nodoc:
300
+ end
301
+
302
+ class ResetField < Field #:nodoc:
303
+ end
304
+
305
+ class SelectField < Field #:nodoc:
306
+
307
+ def find_option(text)
308
+ options.detect { |o| o.matches_text?(text) }
309
+ end
310
+
311
+ protected
312
+
313
+ def default_value
314
+ selected_options = @element.search(".//option[@selected='selected']")
315
+ selected_options = @element.search(".//option[position() = 1]") if selected_options.empty?
316
+
317
+ selected_options.map do |option|
318
+ return "" if option.nil?
319
+ option["value"] || option.inner_html
320
+ end
321
+ end
322
+
323
+ def options
324
+ option_elements.map { |oe| SelectOption.new(self, oe) }
325
+ end
326
+
327
+ def option_elements
328
+ @element.search(".//option")
329
+ end
330
+
331
+ end
332
+ end
@@ -0,0 +1,7 @@
1
+ module Flunk
2
+
3
+ def flunk(message) #:nodoc:
4
+ raise message
5
+ end
6
+
7
+ end
@@ -0,0 +1,130 @@
1
+ require "webrat/core/field"
2
+ require "webrat/core_extensions/blank"
3
+
4
+ module Webrat
5
+ class Form #:nodoc:
6
+ attr_reader :element
7
+
8
+ def initialize(session, element)
9
+ @session = session
10
+ @element = element
11
+ @fields = nil
12
+ end
13
+
14
+ def field(locator, *field_types)
15
+ field_with_id(locator, *field_types) ||
16
+ field_named(locator, *field_types) ||
17
+ field_labeled(locator, *field_types) ||
18
+ nil
19
+ end
20
+
21
+ def find_select_option(option_text)
22
+ select_fields = fields_by_type([SelectField])
23
+
24
+ select_fields.each do |select_field|
25
+ result = select_field.find_option(option_text)
26
+ return result if result
27
+ end
28
+
29
+ nil
30
+ end
31
+
32
+ def find_button(value = nil)
33
+ return fields_by_type([ButtonField]).first if value.nil?
34
+ possible_buttons = fields_by_type([ButtonField])
35
+ possible_buttons.detect { |possible_button| possible_button.matches_id?(value) } ||
36
+ possible_buttons.detect { |possible_button| possible_button.matches_value?(value) }
37
+ end
38
+
39
+ def fields
40
+ return @fields if @fields
41
+
42
+ @fields = (@element.search(".//button", ".//input", ".//textarea", ".//select")).collect do |field_element|
43
+ Field.class_for_element(field_element).new(self, field_element)
44
+ end
45
+ end
46
+
47
+ def submit
48
+ @session.request_page(form_action, form_method, params)
49
+ end
50
+
51
+ def field_with_id(id, *field_types)
52
+ possible_fields = fields_by_type(field_types)
53
+ possible_fields.detect { |possible_field| possible_field.matches_id?(id) }
54
+ end
55
+
56
+ def field_named(name, *field_types)
57
+ possible_fields = fields_by_type(field_types)
58
+ possible_fields.detect { |possible_field| possible_field.matches_name?(name) }
59
+ end
60
+
61
+ def field_labeled(label, *field_types)
62
+ possible_fields = fields_by_type(field_types)
63
+ matching_fields = possible_fields.select do |possible_field|
64
+ possible_field.matches_label?(label)
65
+ end
66
+ matching_fields.min { |a, b| a.label_text.length <=> b.label_text.length }
67
+ end
68
+
69
+ protected
70
+
71
+ def fields_by_type(field_types)
72
+ if field_types.any?
73
+ fields.select { |f| field_types.include?(f.class) }
74
+ else
75
+ fields
76
+ end
77
+ end
78
+
79
+ def params
80
+ all_params = {}
81
+
82
+ fields.each do |field|
83
+ next if field.to_param.nil?
84
+ merge(all_params, field.to_param)
85
+ end
86
+
87
+ all_params
88
+ end
89
+
90
+ def form_method
91
+ @element["method"].blank? ? :get : @element["method"].downcase
92
+ end
93
+
94
+ def form_action
95
+ @element["action"].blank? ? @session.current_url : @element["action"]
96
+ end
97
+
98
+ HASH = [Hash, HashWithIndifferentAccess] rescue [Hash]
99
+
100
+ def merge(all_params, new_param)
101
+ new_param.each do |key, value|
102
+ case all_params[key]
103
+ when *HASH
104
+ merge_hash_values(all_params[key], value)
105
+ when Array
106
+ all_params[key] += value
107
+ else
108
+ all_params[key] = value
109
+ end
110
+ end
111
+ end
112
+
113
+ def merge_hash_values(a, b) # :nodoc:
114
+ a.keys.each do |k|
115
+ if b.has_key?(k)
116
+ case [a[k], b[k]].map{|value| value.class}
117
+ when [Hash, Hash]
118
+ a[k] = merge_hash_values(a[k], b[k])
119
+ b.delete(k)
120
+ when [Array, Array]
121
+ a[k] += b[k]
122
+ b.delete(k)
123
+ end
124
+ end
125
+ end
126
+ a.merge!(b)
127
+ end
128
+
129
+ end
130
+ end