capybara 2.0.3 → 2.1.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.
Files changed (81) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.md +73 -0
  3. data/README.md +52 -5
  4. data/lib/capybara.rb +44 -62
  5. data/lib/capybara/cucumber.rb +4 -1
  6. data/lib/capybara/driver/base.rb +13 -9
  7. data/lib/capybara/driver/node.rb +18 -6
  8. data/lib/capybara/helpers.rb +111 -22
  9. data/lib/capybara/node/actions.rb +24 -19
  10. data/lib/capybara/node/base.rb +25 -32
  11. data/lib/capybara/node/document.rb +6 -2
  12. data/lib/capybara/node/element.rb +45 -7
  13. data/lib/capybara/node/finders.rb +48 -21
  14. data/lib/capybara/node/matchers.rb +38 -21
  15. data/lib/capybara/node/simple.rb +29 -7
  16. data/lib/capybara/query.rb +72 -26
  17. data/lib/capybara/rack_test/browser.rb +11 -3
  18. data/lib/capybara/rack_test/css_handlers.rb +8 -0
  19. data/lib/capybara/rack_test/driver.rb +11 -3
  20. data/lib/capybara/rack_test/form.rb +8 -1
  21. data/lib/capybara/rack_test/node.rb +92 -36
  22. data/lib/capybara/rails.rb +1 -2
  23. data/lib/capybara/result.rb +21 -37
  24. data/lib/capybara/rspec/matchers.rb +60 -31
  25. data/lib/capybara/selector.rb +46 -13
  26. data/lib/capybara/selenium/driver.rb +42 -6
  27. data/lib/capybara/selenium/node.rb +23 -5
  28. data/lib/capybara/session.rb +46 -11
  29. data/lib/capybara/spec/public/test.js +12 -0
  30. data/lib/capybara/spec/session/all_spec.rb +9 -8
  31. data/lib/capybara/spec/session/attach_file_spec.rb +14 -0
  32. data/lib/capybara/spec/session/check_spec.rb +14 -0
  33. data/lib/capybara/spec/session/choose_spec.rb +14 -0
  34. data/lib/capybara/spec/session/click_button_spec.rb +77 -1
  35. data/lib/capybara/spec/session/click_link_or_button_spec.rb +65 -0
  36. data/lib/capybara/spec/session/click_link_spec.rb +24 -0
  37. data/lib/capybara/spec/session/current_scope_spec.rb +29 -0
  38. data/lib/capybara/spec/session/fill_in_spec.rb +14 -0
  39. data/lib/capybara/spec/session/find_button_spec.rb +12 -0
  40. data/lib/capybara/spec/session/find_by_id_spec.rb +12 -1
  41. data/lib/capybara/spec/session/find_field_spec.rb +30 -0
  42. data/lib/capybara/spec/session/find_link_spec.rb +12 -0
  43. data/lib/capybara/spec/session/find_spec.rb +258 -16
  44. data/lib/capybara/spec/session/first_spec.rb +25 -8
  45. data/lib/capybara/spec/session/has_css_spec.rb +2 -2
  46. data/lib/capybara/spec/session/has_field_spec.rb +10 -2
  47. data/lib/capybara/spec/session/has_selector_spec.rb +9 -0
  48. data/lib/capybara/spec/session/has_text_spec.rb +96 -0
  49. data/lib/capybara/spec/session/has_title_spec.rb +47 -0
  50. data/lib/capybara/spec/session/node_spec.rb +43 -0
  51. data/lib/capybara/spec/session/reset_session_spec.rb +10 -2
  52. data/lib/capybara/spec/session/save_page_spec.rb +30 -0
  53. data/lib/capybara/spec/session/select_spec.rb +65 -0
  54. data/lib/capybara/spec/session/text_spec.rb +31 -0
  55. data/lib/capybara/spec/session/title_spec.rb +16 -0
  56. data/lib/capybara/spec/session/uncheck_spec.rb +14 -0
  57. data/lib/capybara/spec/session/unselect_spec.rb +42 -0
  58. data/lib/capybara/spec/session/visit_spec.rb +3 -3
  59. data/lib/capybara/spec/session/within_frame_spec.rb +14 -0
  60. data/lib/capybara/spec/session/within_spec.rb +3 -3
  61. data/lib/capybara/spec/spec_helper.rb +6 -1
  62. data/lib/capybara/spec/views/form.erb +39 -2
  63. data/lib/capybara/spec/views/frame_child.erb +9 -0
  64. data/lib/capybara/spec/views/frame_parent.erb +8 -0
  65. data/lib/capybara/spec/views/with_base_tag.erb +10 -0
  66. data/lib/capybara/spec/views/with_count.erb +7 -0
  67. data/lib/capybara/spec/views/with_hover.erb +17 -0
  68. data/lib/capybara/spec/views/with_html.erb +21 -2
  69. data/lib/capybara/spec/views/with_js.erb +5 -0
  70. data/lib/capybara/spec/views/with_scope.erb +6 -1
  71. data/lib/capybara/spec/views/with_title.erb +1 -0
  72. data/lib/capybara/spec/views/within_frames.erb +1 -0
  73. data/lib/capybara/version.rb +1 -1
  74. data/spec/basic_node_spec.rb +75 -24
  75. data/spec/dsl_spec.rb +2 -1
  76. data/spec/rack_test_spec.rb +4 -3
  77. data/spec/rspec/matchers_spec.rb +105 -17
  78. data/spec/server_spec.rb +8 -8
  79. data/spec/spec_helper.rb +2 -1
  80. metadata +83 -23
  81. metadata.gz.sig +0 -0
@@ -13,7 +13,7 @@ module Capybara
13
13
  # By default it will check if the expression occurs at least once,
14
14
  # but a different number can be specified.
15
15
  #
16
- # page.has_selector?('p#foo', :count => 4)
16
+ # page.has_selector?('p.foo', :count => 4)
17
17
  #
18
18
  # This will check if the expression occurs exactly 4 times.
19
19
  #
@@ -28,8 +28,12 @@ module Capybara
28
28
  # page.has_selector?(:xpath, XPath.descendant(:p))
29
29
  #
30
30
  # @param (see Capybara::Node::Finders#all)
31
- # @option options [Integer] :count (nil) Number of times the expression should occur
32
- # @return [Boolean] If the expression exists
31
+ # @param options a customizable set of options
32
+ # @option options [Integer] :count (nil) Number of times the text should occur
33
+ # @option options [Integer] :minimum (nil) Minimum number of times the text should occur
34
+ # @option options [Integer] :maximum (nil) Maximum number of times the text should occur
35
+ # @option options [Range] :between (nil) Range of times that should contain number of times text occurs
36
+ # @return [Boolean] If the expression exists
33
37
  #
34
38
  def has_selector?(*args)
35
39
  assert_selector(*args)
@@ -71,7 +75,7 @@ module Capybara
71
75
  #
72
76
  # page.assert_selector('li', :text => 'Horse', :visible => true)
73
77
  #
74
- # {assert_selector} can also accept XPath expressions generated by the
78
+ # `assert_selector` can also accept XPath expressions generated by the
75
79
  # XPath gem:
76
80
  #
77
81
  # page.assert_selector(:xpath, XPath.descendant(:p))
@@ -193,17 +197,26 @@ module Capybara
193
197
  # Checks if the page or current node has the given text content,
194
198
  # ignoring any HTML tags and normalizing whitespace.
195
199
  #
196
- # This only matches displayable text and specifically excludes text
197
- # contained within non-display nodes such as script or head tags.
200
+ # By default it will check if the text occurs at least once,
201
+ # but a different number can be specified.
202
+ #
203
+ # page.has_text?('lorem ipsum', between: 2..4)
204
+ #
205
+ # This will check if the text occurs from 2 to 4 times.
198
206
  #
199
- # @param [String] content The text to check for
200
- # @return [Boolean] Whether it exists
207
+ # @overload has_text?([type], text, [options])
208
+ # @param [:all, :visible] type Whether to check for only visible or all text
209
+ # @param [String, Regexp] text The text/regexp to check for
210
+ # @param [Hash] options additional options
211
+ # @option options [Integer] :count (nil) Number of times the text should occur
212
+ # @option options [Integer] :minimum (nil) Minimum number of times the text should occur
213
+ # @option options [Integer] :maximum (nil) Maximum number of times the text should occur
214
+ # @option options [Range] :between (nil) Range of times that should contain number of times text occurs
215
+ # @return [Boolean] Whether it exists
201
216
  #
202
- def has_text?(content)
217
+ def has_text?(*args)
203
218
  synchronize do
204
- unless Capybara::Helpers.normalize_whitespace(text).match(Capybara::Helpers.to_regexp(content))
205
- raise ExpectationNotMet
206
- end
219
+ raise ExpectationNotMet unless text_found?(*args)
207
220
  end
208
221
  return true
209
222
  rescue Capybara::ExpectationNotMet
@@ -216,17 +229,12 @@ module Capybara
216
229
  # Checks if the page or current node does not have the given text
217
230
  # content, ignoring any HTML tags and normalizing whitespace.
218
231
  #
219
- # This only matches displayable text and specifically excludes text
220
- # contained within non-display nodes such as script or head tags.
232
+ # @param (see #has_text?)
233
+ # @return [Boolean] Whether it doesn't exist
221
234
  #
222
- # @param [String] content The text to check for
223
- # @return [Boolean] Whether it doesn't exist
224
- #
225
- def has_no_text?(content)
235
+ def has_no_text?(*args)
226
236
  synchronize do
227
- if Capybara::Helpers.normalize_whitespace(text).match(Capybara::Helpers.to_regexp(content))
228
- raise ExpectationNotMet
229
- end
237
+ raise ExpectationNotMet if text_found?(*args)
230
238
  end
231
239
  return true
232
240
  rescue Capybara::ExpectationNotMet
@@ -299,6 +307,8 @@ module Capybara
299
307
  #
300
308
  # page.has_field?('Email', :type => 'email')
301
309
  #
310
+ # Note: 'textarea' and 'select' are valid type values, matching the associated tag names.
311
+ #
302
312
  # @param [String] locator The label, name or id of a field to check for
303
313
  # @option options [String] :with The text content of the field
304
314
  # @option options [String] :type The type attribute of the field
@@ -450,6 +460,13 @@ module Capybara
450
460
 
451
461
  private
452
462
 
463
+ def text_found?(*args)
464
+ type = args.shift if args.first.is_a?(Symbol) or args.first.nil?
465
+ content, options = args
466
+ count = Capybara::Helpers.normalize_whitespace(text(type)).scan(Capybara::Helpers.to_regexp(content)).count
467
+
468
+ Capybara::Helpers.matches_count?(count, options || {})
469
+ end
453
470
  end
454
471
  end
455
472
  end
@@ -26,7 +26,7 @@ module Capybara
26
26
  #
27
27
  # @return [String] The text of the element
28
28
  #
29
- def text
29
+ def text(type=nil)
30
30
  native.text
31
31
  end
32
32
 
@@ -108,6 +108,15 @@ module Capybara
108
108
  native[:checked]
109
109
  end
110
110
 
111
+ ##
112
+ #
113
+ # Whether or not the element is disabled.
114
+ #
115
+ # @return [Boolean] Whether the element is disabled
116
+ def disabled?
117
+ native[:disabled]
118
+ end
119
+
111
120
  ##
112
121
  #
113
122
  # Whether or not the element is selected.
@@ -118,7 +127,7 @@ module Capybara
118
127
  native[:selected]
119
128
  end
120
129
 
121
- def synchronize
130
+ def synchronize(seconds=nil)
122
131
  yield # simple nodes don't need to wait
123
132
  end
124
133
 
@@ -126,13 +135,26 @@ module Capybara
126
135
  # no op
127
136
  end
128
137
 
129
- def unsynchronized
130
- yield # simple nodes don't need to wait
138
+ def title
139
+ native.xpath("//title").first.text
131
140
  end
132
141
 
133
- def all(*args)
134
- query = Capybara::Query.new(*args)
135
- elements = native.xpath(query.xpath).map do |node|
142
+ def has_title?(content)
143
+ title.match(Capybara::Helpers.to_regexp(content))
144
+ end
145
+
146
+ def has_no_title?(content)
147
+ not has_title?(content)
148
+ end
149
+
150
+ private
151
+
152
+ def resolve_query(query, exact=nil)
153
+ elements = if query.selector.format == :css
154
+ native.css(query.css)
155
+ else
156
+ native.xpath(query.xpath(exact))
157
+ end.map do |node|
136
158
  self.class.new(node)
137
159
  end
138
160
  Capybara::Result.new(elements, query)
@@ -1,16 +1,13 @@
1
1
  module Capybara
2
2
  class Query
3
- attr_accessor :selector, :locator, :options, :xpath, :find, :negative
3
+ attr_accessor :selector, :locator, :options, :expression, :find, :negative
4
4
 
5
- VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum]
5
+ VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait]
6
+ VALID_MATCH = [:first, :smart, :prefer_exact, :one]
6
7
 
7
8
  def initialize(*args)
8
9
  @options = if args.last.is_a?(Hash) then args.pop.dup else {} end
9
10
 
10
- unless options.has_key?(:visible)
11
- @options[:visible] = Capybara.ignore_hidden_elements
12
- end
13
-
14
11
  if args[0].is_a?(Symbol)
15
12
  @selector = Selector.all[args[0]]
16
13
  @locator = args[1]
@@ -20,8 +17,12 @@ module Capybara
20
17
  end
21
18
  @selector ||= Selector.all[Capybara.default_selector]
22
19
 
23
- @xpath = @selector.call(@locator).to_s
20
+ # for compatibility with Capybara 2.0
21
+ if Capybara.exact_options and @selector == Selector.all[:option]
22
+ @options[:exact] = true
23
+ end
24
24
 
25
+ @expression = @selector.call(@locator)
25
26
  assert_valid_keys!
26
27
  end
27
28
 
@@ -35,34 +36,76 @@ module Capybara
35
36
  end
36
37
 
37
38
  def matches_filters?(node)
38
- node.unsynchronized do
39
- if options[:text]
40
- regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text])
41
- return false if not node.text.match(regexp)
39
+ if options[:text]
40
+ regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text])
41
+ return false if not node.text(visible).match(regexp)
42
+ end
43
+ case visible
44
+ when :visible then return false unless node.visible?
45
+ when :hidden then return false if node.visible?
46
+ end
47
+ selector.custom_filters.each do |name, filter|
48
+ if options.has_key?(name)
49
+ return false unless filter.matches?(node, options[name])
50
+ elsif filter.default?
51
+ return false unless filter.matches?(node, filter.default)
42
52
  end
43
- return false if options[:visible] and not node.visible?
44
- selector.custom_filters.each do |name, block|
45
- return false if options.has_key?(name) and not block.call(node, options[name])
53
+ end
54
+ end
55
+
56
+ def visible
57
+ if options.has_key?(:visible)
58
+ case @options[:visible]
59
+ when true then :visible
60
+ when false then :all
61
+ else @options[:visible]
62
+ end
63
+ else
64
+ if Capybara.ignore_hidden_elements
65
+ :visible
66
+ else
67
+ :all
46
68
  end
47
- true
48
69
  end
49
70
  end
50
71
 
51
- def matches_count?(count)
52
- case
53
- when options[:between]
54
- options[:between] === count
55
- when options[:count]
56
- options[:count].to_i == count
57
- when options[:maximum]
58
- options[:maximum].to_i >= count
59
- when options[:minimum]
60
- options[:minimum].to_i <= count
72
+ def wait
73
+ if options.has_key?(:wait)
74
+ @options[:wait] or 0
61
75
  else
62
- count > 0
76
+ Capybara.default_wait_time
63
77
  end
64
78
  end
65
79
 
80
+ def exact?
81
+ if options.has_key?(:exact)
82
+ @options[:exact]
83
+ else
84
+ Capybara.exact
85
+ end
86
+ end
87
+
88
+ def match
89
+ if options.has_key?(:match)
90
+ @options[:match]
91
+ else
92
+ Capybara.match
93
+ end
94
+ end
95
+
96
+ def xpath(exact=nil)
97
+ exact = self.exact? if exact == nil
98
+ if @expression.respond_to?(:to_xpath) and exact
99
+ @expression.to_xpath(:exact)
100
+ else
101
+ @expression.to_s
102
+ end
103
+ end
104
+
105
+ def css
106
+ @expression
107
+ end
108
+
66
109
  private
67
110
 
68
111
  def assert_valid_keys!
@@ -73,6 +116,9 @@ module Capybara
73
116
  valid_names = valid_keys.map(&:inspect).join(", ")
74
117
  raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
75
118
  end
119
+ unless VALID_MATCH.include?(match)
120
+ raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(", ")}"
121
+ end
76
122
  end
77
123
  end
78
124
  end
@@ -80,8 +80,12 @@ class Capybara::RackTest::Browser
80
80
  @dom ||= Nokogiri::HTML(html)
81
81
  end
82
82
 
83
- def find(selector)
84
- dom.xpath(selector).map { |node| Capybara::RackTest::Node.new(self, node) }
83
+ def find(format, selector)
84
+ if format==:css
85
+ dom.css(selector, Capybara::RackTest::CSSHandlers.new)
86
+ else
87
+ dom.xpath(selector)
88
+ end.map { |node| Capybara::RackTest::Node.new(self, node) }
85
89
  end
86
90
 
87
91
  def html
@@ -89,7 +93,11 @@ class Capybara::RackTest::Browser
89
93
  rescue Rack::Test::Error
90
94
  ""
91
95
  end
92
-
96
+
97
+ def title
98
+ dom.xpath("//title").text
99
+ end
100
+
93
101
  protected
94
102
 
95
103
  def build_rack_mock_session
@@ -0,0 +1,8 @@
1
+ class Capybara::RackTest::CSSHandlers
2
+ def disabled list
3
+ list.find_all { |node| node.has_attribute? 'disabled' }
4
+ end
5
+ def enabled list
6
+ list.find_all { |node| !node.has_attribute? 'disabled' }
7
+ end
8
+ end
@@ -62,10 +62,14 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
62
62
  response.status
63
63
  end
64
64
 
65
- def find(selector)
66
- browser.find(selector)
65
+ def find_xpath(selector)
66
+ browser.find(:xpath, selector)
67
67
  end
68
-
68
+
69
+ def find_css(selector)
70
+ browser.find(:css,selector)
71
+ end
72
+
69
73
  def html
70
74
  browser.html
71
75
  end
@@ -73,6 +77,10 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base
73
77
  def dom
74
78
  browser.dom
75
79
  end
80
+
81
+ def title
82
+ browser.title
83
+ end
76
84
 
77
85
  def reset!
78
86
  @browser = nil
@@ -17,7 +17,14 @@ class Capybara::RackTest::Form < Capybara::RackTest::Node
17
17
  def params(button)
18
18
  params = {}
19
19
 
20
- native.xpath("(.//input|.//select|.//textarea)[not(@disabled)]").map do |field|
20
+ form_element_types=[:input, :select, :textarea]
21
+ form_elements_xpath=XPath.generate do |x|
22
+ xpath=x.descendant(*form_element_types).where(~x.attr(:form))
23
+ xpath=xpath.union(x.anywhere(*form_element_types).where(x.attr(:form) == native[:id])) if native[:id]
24
+ xpath.where(~x.attr(:disabled))
25
+ end.to_s
26
+
27
+ native.xpath(form_elements_xpath).map do |field|
21
28
  case field.name
22
29
  when 'input'
23
30
  if %w(radio checkbox).include? field['type']
@@ -1,5 +1,9 @@
1
1
  class Capybara::RackTest::Node < Capybara::Driver::Node
2
- def text
2
+ def all_text
3
+ Capybara::Helpers.normalize_whitespace(native.text)
4
+ end
5
+
6
+ def visible_text
3
7
  Capybara::Helpers.normalize_whitespace(unnormalized_text)
4
8
  end
5
9
 
@@ -15,42 +19,21 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
15
19
  if (Array === value) && !self[:multiple]
16
20
  raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
17
21
  end
18
- if tag_name == 'input' and type == 'radio'
19
- other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
20
- driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
21
- native['checked'] = 'checked'
22
- elsif tag_name == 'input' and type == 'checkbox'
23
- if value && !native['checked']
24
- native['checked'] = 'checked'
25
- elsif !value && native['checked']
26
- native.remove_attribute('checked')
27
- end
28
- elsif tag_name == 'input'
29
- if (type == 'text' || type == 'password') && self[:maxlength] &&
30
- !self[:maxlength].empty?
31
- # Browser behavior for maxlength="0" is inconsistent, so we stick with
32
- # Firefox, allowing no input
33
- value = value[0...self[:maxlength].to_i]
34
- end
35
- if Array === value #Assert multiple attribute is present
36
- value.each do |v|
37
- new_native = native.clone
38
- new_native.remove_attribute('value')
39
- native.add_next_sibling(new_native)
40
- new_native['value'] = v.to_s
41
- end
42
- native.remove
43
- else
44
- native['value'] = value.to_s
45
- end
46
- elsif tag_name == "textarea"
47
- native.content = value.to_s
22
+
23
+ if radio?
24
+ set_radio(value)
25
+ elsif checkbox?
26
+ set_checkbox(value)
27
+ elsif input_field?
28
+ set_input(value)
29
+ elsif textarea?
30
+ native.content = value.to_s unless self[:readonly]
48
31
  end
49
32
  end
50
33
 
51
34
  def select_option
52
35
  if select_node['multiple'] != 'multiple'
53
- select_node.find(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
36
+ select_node.find_xpath(".//option[@selected]").each { |node| node.native.remove_attribute("selected") }
54
37
  end
55
38
  native["selected"] = 'selected'
56
39
  end
@@ -89,14 +72,26 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
89
72
  string_node.selected?
90
73
  end
91
74
 
75
+ def disabled?
76
+ if %w(option optgroup).include? tag_name
77
+ string_node.disabled? || find_xpath("parent::*")[0].disabled?
78
+ else
79
+ string_node.disabled?
80
+ end
81
+ end
82
+
92
83
  def path
93
84
  native.path
94
85
  end
95
86
 
96
- def find(locator)
87
+ def find_xpath(locator)
97
88
  native.xpath(locator).map { |n| self.class.new(driver, n) }
98
89
  end
99
-
90
+
91
+ def find_css(locator)
92
+ native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |n| self.class.new(driver, n) }
93
+ end
94
+
100
95
  def ==(other)
101
96
  native == other.native
102
97
  end
@@ -125,7 +120,7 @@ private
125
120
 
126
121
  # a reference to the select node if this is an option node
127
122
  def select_node
128
- find('./ancestor::select').first
123
+ find_xpath('./ancestor::select').first
129
124
  end
130
125
 
131
126
  def type
@@ -133,6 +128,67 @@ private
133
128
  end
134
129
 
135
130
  def form
136
- native.ancestors('form').first
131
+ if native[:form]
132
+ native.xpath("//form[@id='#{native[:form]}']").first
133
+ else
134
+ native.ancestors('form').first
135
+ end
136
+ end
137
+
138
+ def set_radio(value)
139
+ other_radios_xpath = XPath.generate { |x| x.anywhere(:input)[x.attr(:name).equals(self[:name])] }.to_s
140
+ driver.dom.xpath(other_radios_xpath).each { |node| node.remove_attribute("checked") }
141
+ native['checked'] = 'checked'
142
+ end
143
+
144
+ def set_checkbox(value)
145
+ if value && !native['checked']
146
+ native['checked'] = 'checked'
147
+ elsif !value && native['checked']
148
+ native.remove_attribute('checked')
149
+ end
150
+ end
151
+
152
+ def set_input(value)
153
+ if text_or_password? && attribute_is_not_blank?(:maxlength)
154
+ # Browser behavior for maxlength="0" is inconsistent, so we stick with
155
+ # Firefox, allowing no input
156
+ value = value[0...self[:maxlength].to_i]
157
+ end
158
+ if Array === value #Assert multiple attribute is present
159
+ value.each do |v|
160
+ new_native = native.clone
161
+ new_native.remove_attribute('value')
162
+ native.add_next_sibling(new_native)
163
+ new_native['value'] = v.to_s
164
+ end
165
+ native.remove
166
+ else
167
+ native['value'] = value.to_s unless self[:readonly]
168
+ end
169
+ end
170
+
171
+ def attribute_is_not_blank?(attribute)
172
+ self[attribute] && !self[attribute].empty?
173
+ end
174
+
175
+ def checkbox?
176
+ input_field? && type == 'checkbox'
177
+ end
178
+
179
+ def input_field?
180
+ tag_name == 'input'
181
+ end
182
+
183
+ def radio?
184
+ input_field? && type == 'radio'
185
+ end
186
+
187
+ def textarea?
188
+ tag_name == "textarea"
189
+ end
190
+
191
+ def text_or_password?
192
+ input_field? && (type == 'text' || type == 'password')
137
193
  end
138
194
  end