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
@@ -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