oki-celerity 0.8.1.dev

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 (77) hide show
  1. data/HISTORY +111 -0
  2. data/LICENSE +621 -0
  3. data/README.rdoc +80 -0
  4. data/Rakefile +11 -0
  5. data/VERSION.yml +5 -0
  6. data/celerity.gemspec +126 -0
  7. data/lib/celerity/browser.rb +908 -0
  8. data/lib/celerity/clickable_element.rb +73 -0
  9. data/lib/celerity/collections.rb +164 -0
  10. data/lib/celerity/container.rb +800 -0
  11. data/lib/celerity/default_viewer.rb +14 -0
  12. data/lib/celerity/disabled_element.rb +40 -0
  13. data/lib/celerity/element.rb +311 -0
  14. data/lib/celerity/element_collection.rb +107 -0
  15. data/lib/celerity/element_locator.rb +164 -0
  16. data/lib/celerity/elements/button.rb +54 -0
  17. data/lib/celerity/elements/file_field.rb +29 -0
  18. data/lib/celerity/elements/form.rb +22 -0
  19. data/lib/celerity/elements/frame.rb +86 -0
  20. data/lib/celerity/elements/image.rb +89 -0
  21. data/lib/celerity/elements/label.rb +16 -0
  22. data/lib/celerity/elements/link.rb +43 -0
  23. data/lib/celerity/elements/meta.rb +14 -0
  24. data/lib/celerity/elements/non_control_elements.rb +124 -0
  25. data/lib/celerity/elements/option.rb +38 -0
  26. data/lib/celerity/elements/radio_check.rb +114 -0
  27. data/lib/celerity/elements/select_list.rb +146 -0
  28. data/lib/celerity/elements/table.rb +153 -0
  29. data/lib/celerity/elements/table_cell.rb +36 -0
  30. data/lib/celerity/elements/table_elements.rb +42 -0
  31. data/lib/celerity/elements/table_row.rb +49 -0
  32. data/lib/celerity/elements/text_field.rb +168 -0
  33. data/lib/celerity/exception.rb +83 -0
  34. data/lib/celerity/htmlunit/apache-mime4j-0.6.jar +0 -0
  35. data/lib/celerity/htmlunit/commons-codec-1.4.jar +0 -0
  36. data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
  37. data/lib/celerity/htmlunit/commons-io-1.4.jar +0 -0
  38. data/lib/celerity/htmlunit/commons-lang-2.5.jar +0 -0
  39. data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
  40. data/lib/celerity/htmlunit/cssparser-0.9.5.jar +0 -0
  41. data/lib/celerity/htmlunit/htmlunit-2.9-SNAPSHOT.jar +0 -0
  42. data/lib/celerity/htmlunit/htmlunit-core-js-2.8.jar +0 -0
  43. data/lib/celerity/htmlunit/httpclient-4.0.1.jar +0 -0
  44. data/lib/celerity/htmlunit/httpcore-4.0.1.jar +0 -0
  45. data/lib/celerity/htmlunit/httpmime-4.0.1.jar +0 -0
  46. data/lib/celerity/htmlunit/nekohtml-1.9.14.jar +0 -0
  47. data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
  48. data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
  49. data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
  50. data/lib/celerity/htmlunit/xercesImpl-2.9.1.jar +0 -0
  51. data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
  52. data/lib/celerity/htmlunit.rb +64 -0
  53. data/lib/celerity/identifier.rb +28 -0
  54. data/lib/celerity/ignoring_web_connection.rb +15 -0
  55. data/lib/celerity/input_element.rb +25 -0
  56. data/lib/celerity/javascript_debugger.rb +32 -0
  57. data/lib/celerity/listener.rb +143 -0
  58. data/lib/celerity/resources/no_viewer.png +0 -0
  59. data/lib/celerity/short_inspect.rb +20 -0
  60. data/lib/celerity/util.rb +129 -0
  61. data/lib/celerity/version.rb +3 -0
  62. data/lib/celerity/viewer_connection.rb +89 -0
  63. data/lib/celerity/watir_compatibility.rb +70 -0
  64. data/lib/celerity/xpath_support.rb +52 -0
  65. data/lib/celerity.rb +77 -0
  66. data/tasks/benchmark.rake +4 -0
  67. data/tasks/check.rake +24 -0
  68. data/tasks/clean.rake +3 -0
  69. data/tasks/fix.rake +25 -0
  70. data/tasks/jar.rake +55 -0
  71. data/tasks/jeweler.rake +28 -0
  72. data/tasks/rdoc.rake +4 -0
  73. data/tasks/snapshot.rake +25 -0
  74. data/tasks/spec.rake +26 -0
  75. data/tasks/website.rake +10 -0
  76. data/tasks/yard.rake +16 -0
  77. metadata +204 -0
@@ -0,0 +1,14 @@
1
+ module Celerity
2
+ class DefaultViewer
3
+ IMAGE = "#{Celerity::DIR}/resources/no_viewer.png"
4
+
5
+ class << self
6
+ def save(path = nil)
7
+ return unless path
8
+ FileUtils.copy(IMAGE, path)
9
+ end
10
+
11
+ def close; end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ module Celerity
2
+
3
+ #
4
+ # Mixed in to all elements that can have the 'disabled' attribute.
5
+ #
6
+
7
+ module DisabledElement
8
+ include Celerity::Exception
9
+
10
+ #
11
+ # Returns false if the element is disabled.
12
+ #
13
+
14
+ def enabled?
15
+ !disabled?
16
+ end
17
+
18
+ #
19
+ # Returns true if the element is disabled.
20
+ #
21
+
22
+ def disabled?
23
+ assert_exists unless defined?(@object) && @object
24
+ @object.isDisabled
25
+ end
26
+ alias_method :disabled, :disabled?
27
+
28
+ #
29
+ # Used internally.
30
+ # @api private
31
+ #
32
+
33
+ def assert_enabled
34
+ if disabled?
35
+ raise ObjectDisabledException, "Object #{identifier_string} is disabled"
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,311 @@
1
+ module Celerity
2
+
3
+ #
4
+ # Superclass for all HTML elements.
5
+ #
6
+
7
+ class Element
8
+ include Exception
9
+ include Container
10
+
11
+ attr_reader :container
12
+
13
+ # HTML 4.01 Transitional DTD
14
+ HTML_401_TRANSITIONAL = {
15
+ :core => [:class, :id, :style, :title],
16
+ :cell_halign => [:align, :char, :charoff],
17
+ :cell_valign => [:valign],
18
+ :i18n => [:dir, :lang],
19
+ :event => [:onclick, :ondblclick, :onmousedown, :onmouseup, :onmouseover,
20
+ :onmousemove, :onmouseout, :onkeypress, :onkeydown, :onkeyup],
21
+ :sloppy => [:name, :value]
22
+ }
23
+
24
+ CELLHALIGN_ATTRIBUTES = HTML_401_TRANSITIONAL[:cell_halign]
25
+ CELLVALIGN_ATTRIBUTES = HTML_401_TRANSITIONAL[:cell_valign]
26
+ BASE_ATTRIBUTES = HTML_401_TRANSITIONAL.values_at(:core, :i18n, :event, :sloppy).flatten
27
+ ATTRIBUTES = BASE_ATTRIBUTES
28
+ TAGS = []
29
+
30
+ DEFAULT_HOW = nil
31
+
32
+ # @api private
33
+ def initialize(container, *args)
34
+ self.container = container
35
+
36
+ case args.size
37
+ when 2
38
+ @conditions = { args[0] => args[1] }
39
+ when 1
40
+ if args.first.is_a? Hash
41
+ @conditions = args.first
42
+ elsif (how = self.class::DEFAULT_HOW)
43
+ @conditions = { how => args.first }
44
+ else
45
+ raise ArgumentError, "wrong number of arguments (1 for 2)"
46
+ end
47
+ when 0
48
+ @conditions = { :index => Celerity.index_offset }
49
+ else
50
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
51
+ end
52
+
53
+ @conditions.freeze
54
+ @object = nil
55
+ end
56
+
57
+ def ==(other)
58
+ return false unless other.kind_of? Element
59
+ xpath == other.xpath
60
+ end
61
+
62
+ #
63
+ # Get the parent element
64
+ # @return [Celerity::Element, nil] subclass of Celerity::Element, or nil if no parent was found
65
+ #
66
+
67
+ def parent
68
+ assert_exists
69
+
70
+ obj = @object.parentNode
71
+ until element_class = Celerity::Util.htmlunit2celerity(obj.class)
72
+ return nil if obj.nil?
73
+ obj = obj.parentNode
74
+ end
75
+
76
+ element_class.new(@container, :object, obj)
77
+ end
78
+
79
+ #
80
+ # Sets the focus to this element.
81
+ #
82
+
83
+ def focus
84
+ assert_exists
85
+ @object.focus
86
+ end
87
+
88
+ #
89
+ # Returns true if this element is the currently focused element
90
+ #
91
+ # Celerity-specific.
92
+ #
93
+
94
+ def focused?
95
+ assert_exists
96
+ @object == browser.page.getFocusedElement
97
+ end
98
+
99
+ #
100
+ # Fires the given event for this element
101
+ #
102
+
103
+ def fire_event(event_name)
104
+ assert_exists
105
+ @object.fireEvent(event_name.sub(/^on/, ''))
106
+ end
107
+
108
+ #
109
+ # Used internally. Find the element on the page.
110
+ # @api private
111
+ #
112
+
113
+ def locate
114
+ @object = ElementLocator.new(@container, self.class).find_by_conditions(@conditions)
115
+ end
116
+
117
+ #
118
+ # Returns the HtmlUnit object backing this element
119
+ #
120
+
121
+ def object
122
+ @object || locate
123
+ end
124
+
125
+ #
126
+ # Returns a JavaScript object representing the receiver
127
+ #
128
+ # @api internal - subject to change
129
+ #
130
+
131
+ def javascript_object
132
+ assert_exists
133
+ @object.getScriptObject
134
+ end
135
+
136
+ #
137
+ # @return [String] A string representation of the element.
138
+ #
139
+
140
+ def to_s
141
+ assert_exists
142
+ Celerity::Util.create_string @object
143
+ end
144
+
145
+ #
146
+ # @param [String, #to_s] The attribute.
147
+ # @return [String] The value of the given attribute.
148
+ #
149
+
150
+ def attribute_value(attribute)
151
+ assert_exists
152
+ @object.getAttribute attribute.to_s
153
+ end
154
+
155
+ #
156
+ # Check if the element is visible to the user or not.
157
+ # Note that this only takes the _style attribute_ of the element (and
158
+ # its parents) into account - styles from applied CSS is not considered.
159
+ #
160
+ # @return [boolean]
161
+ #
162
+
163
+ def visible?
164
+ assert_exists
165
+ @object.isDisplayed
166
+ end
167
+
168
+ #
169
+ # Used internally to ensure the element actually exists.
170
+ #
171
+ # @raise [Celerity::Exception::UnknownObjectException] if the element can't be found.
172
+ # @api private
173
+ #
174
+
175
+ def assert_exists
176
+ locate unless @object
177
+ unless @object
178
+ raise UnknownObjectException, "Unable to locate #{self.class.name[/::(.*)$/, 1]}, using #{identifier_string}"
179
+ end
180
+ end
181
+
182
+ #
183
+ # Checks if the element exists.
184
+ # @return [true, false]
185
+ #
186
+
187
+ def exists?
188
+ assert_exists
189
+ true
190
+ rescue UnknownObjectException, UnknownFrameException
191
+ false
192
+ end
193
+ alias_method :exist?, :exists?
194
+
195
+ #
196
+ # Return a text representation of the element as it would appear in a browser.
197
+ #
198
+ # @see inner_text
199
+ # @return [String]
200
+ #
201
+
202
+ def text
203
+ assert_exists
204
+ @object.asText.strip # this must behave like ElementLocator
205
+ end
206
+
207
+ #
208
+ # Return the text content of this DOM node, disregarding its visibility.
209
+ #
210
+ # (Celerity-specific?)
211
+ #
212
+ # @see text
213
+ # @return [String]
214
+ #
215
+
216
+ def inner_text
217
+ assert_exists
218
+ Celerity::Util.normalize_text @object.getTextContent
219
+ end
220
+
221
+ #
222
+ # @return [String] The normative XML representation of the element (including children).
223
+ #
224
+
225
+ def to_xml
226
+ assert_exists
227
+ @object.asXml
228
+ end
229
+ alias_method :asXml, :to_xml
230
+ alias_method :as_xml, :to_xml
231
+ alias_method :html, :to_xml
232
+
233
+ #
234
+ # @return [String] A string representation of the element's attributes.
235
+ #
236
+
237
+ def attribute_string
238
+ assert_exists
239
+
240
+ result = ''
241
+ @object.getAttributes.each do |attribute|
242
+ result << %Q{#{attribute.getName}="#{attribute.getValue}"}
243
+ end
244
+
245
+ result
246
+ end
247
+
248
+ #
249
+ # return the canonical xpath for this element (Celerity-specific)
250
+ #
251
+
252
+ def xpath
253
+ assert_exists
254
+ @object.getCanonicalXPath
255
+ end
256
+
257
+ #
258
+ # Dynamically get element attributes.
259
+ #
260
+ # @see ATTRIBUTES constant for a list of valid methods for a given element.
261
+ #
262
+ # @return [String] The resulting attribute.
263
+ # @raise [NoMethodError] if the element doesn't support this attribute.
264
+ #
265
+
266
+ def method_missing(meth, *args, &blk)
267
+ assert_exists
268
+
269
+ meth = selector_to_attribute(meth)
270
+
271
+ if self.class::ATTRIBUTES.include?(meth) || (self.class == Element && @object.hasAttribute(meth.to_s))
272
+ return @object.getAttribute(meth.to_s)
273
+ end
274
+ Log.warn "Element\#method_missing calling super with #{meth.inspect}"
275
+ super
276
+ end
277
+
278
+ def methods(*args)
279
+ ms = super
280
+ ms += self.class::ATTRIBUTES.map { |e| e.to_s }
281
+ ms.sort
282
+ end
283
+
284
+ def respond_to?(meth, include_private = false)
285
+ meth = selector_to_attribute(meth)
286
+ return true if self.class::ATTRIBUTES.include?(meth)
287
+ super
288
+ end
289
+
290
+ private
291
+
292
+ def identifier_string
293
+ if @conditions.size == 1
294
+ how, what = @conditions.to_a.first
295
+ "#{how.inspect} and #{what.inspect}"
296
+ else
297
+ @conditions.inspect
298
+ end
299
+ end
300
+
301
+ def selector_to_attribute(meth)
302
+ case meth
303
+ when :class_name then :class
304
+ when :caption then :value
305
+ when :url then :href
306
+ else meth
307
+ end
308
+ end
309
+
310
+ end # Element
311
+ end # Celerity
@@ -0,0 +1,107 @@
1
+ module Celerity
2
+
3
+ #
4
+ # This class is the superclass for the iterator classes (Buttons, Links, Spans etc.)
5
+ # It would normally only be accessed by the iterator methods (Browser#spans, Browser#links, ...).
6
+ #
7
+
8
+ class ElementCollection
9
+ include Enumerable
10
+
11
+ #
12
+ # @api private
13
+ #
14
+
15
+ def initialize(container, how = nil, what = nil)
16
+ @container = container
17
+ @object = (how == :object ? what : nil)
18
+ @length = length
19
+ end
20
+
21
+ #
22
+ # @return [Fixnum] The number of elements in this collection.
23
+ #
24
+
25
+ def length
26
+ if @object
27
+ @object.length
28
+ else
29
+ @elements ||= ElementLocator.new(@container, element_class).elements_by_idents
30
+ @elements.size
31
+ end
32
+ end
33
+ alias_method :size, :length
34
+
35
+ #
36
+ # @yieldparam [Celerity::Element] element Iterate through the elements in this collection.
37
+ #
38
+
39
+ def each
40
+ if @elements
41
+ @elements.each { |e| yield(element_class.new(@container, :object, e)) }
42
+ else
43
+ 0.upto(@length - 1) { |i| yield iterator_object(i) }
44
+ end
45
+
46
+ @length
47
+ end
48
+
49
+ #
50
+ # Get the element at the given index.
51
+ # By default, this is 1-indexed to keep compatibility with Watir.
52
+ #
53
+ # Also note that because of Watir's lazy loading, this will return an Element
54
+ # instance even if the index is out of bounds.
55
+ #
56
+ # @param [Fixnum] n Index of wanted element, 1-indexed unless Celerity.index_offset is changed.
57
+ # @return [Celerity::Element] Returns a subclass of Celerity::Element
58
+ #
59
+
60
+ def [](n)
61
+ if @elements && @elements[n - Celerity.index_offset]
62
+ element_class.new(@container, :object, @elements[n - Celerity.index_offset])
63
+ else
64
+ iterator_object(n - Celerity.index_offset)
65
+ end
66
+ end
67
+
68
+ #
69
+ # Get the first element in this collection. (Celerity-specific)
70
+ #
71
+ # @return [Celerity::Element] Returns a subclass of Celerity::Element
72
+ #
73
+
74
+ def first
75
+ self[Celerity.index_offset]
76
+ end
77
+
78
+ #
79
+ # Get the last element in this collection. (Celerity-specific)
80
+ #
81
+ # @return [Celerity::Element] Returns a subclass of Celerity::Element
82
+ #
83
+
84
+ def last
85
+ self[Celerity.index_offset - 1]
86
+ end
87
+
88
+ #
89
+ # Note: This can be quite useful in irb:
90
+ #
91
+ # puts browser.text_fields
92
+ #
93
+ # @return [String] A string representation of all elements in this collection.
94
+ #
95
+
96
+ def to_s
97
+ map { |e| e.to_s }.join("\n")
98
+ end
99
+
100
+ private
101
+
102
+ def iterator_object(i)
103
+ element_class.new(@container, :index, i + Celerity.index_offset)
104
+ end
105
+
106
+ end # ElementCollection
107
+ end # Celerity
@@ -0,0 +1,164 @@
1
+ module Celerity
2
+
3
+ #
4
+ # Used internally to locate elements on the page.
5
+ #
6
+
7
+ class ElementLocator
8
+ include Celerity::Exception
9
+ attr_accessor :idents
10
+
11
+
12
+ def initialize(container, element_class)
13
+ container.assert_exists
14
+
15
+ @container = container
16
+ @object = container.object
17
+ @element_class = element_class
18
+ @attributes = @element_class::ATTRIBUTES # could check for 'strict' here?
19
+ @idents = @element_class::TAGS
20
+ @tags = @idents.map { |e| e.tag.downcase }
21
+ end
22
+
23
+ def find_by_conditions(conditions) # TODO: refactor without performance hit
24
+ return nil unless @object # probably means we're on a TextPage (content-type is "text/plain")
25
+
26
+ @condition_idents = []
27
+ attributes = Hash.new { |h, k| h[k] = [] }
28
+ index = 0 # by default, return the first matching element
29
+ text = nil
30
+
31
+ conditions.each do |how, what|
32
+ case how
33
+ when :object
34
+ unless what.is_a?(HtmlUnit::Html::HtmlElement) || what.nil?
35
+ raise ArgumentError, "expected an HtmlUnit::Html::HtmlElement subclass, got #{what.inspect}:#{what.class}"
36
+ end
37
+ return what
38
+ when :xpath
39
+ return find_by_xpath(what)
40
+ when :label
41
+ return find_by_label(what) unless @attributes.include?(:label)
42
+ when :class_name
43
+ how = :class
44
+ when :url
45
+ how = :href
46
+ when :caption
47
+ how = :text
48
+ end
49
+
50
+ if how == :id && conditions.size == 1
51
+ return find_by_id(what)
52
+ elsif @attributes.include?(how = how.to_sym)
53
+ attributes[how] << what
54
+ elsif how == :index
55
+ index = what.to_i - Celerity.index_offset
56
+ elsif how == :text
57
+ text = what
58
+ else
59
+ raise MissingWayOfFindingObjectException, "No how #{how.inspect}"
60
+ end
61
+ end
62
+
63
+ @idents.each do |ident|
64
+ merged = attributes.merge(ident.attributes) { |key, v1, v2| v1 | v2 }
65
+ id = Identifier.new(ident.tag, merged)
66
+ id.text = ident.text || text # «original» identifier takes precedence for :text
67
+ @condition_idents << id
68
+ end
69
+
70
+ if index == 0
71
+ element_by_idents(@condition_idents)
72
+ else
73
+ elements_by_idents(@condition_idents)[index]
74
+ end
75
+
76
+ rescue HtmlUnit::ElementNotFoundException
77
+ nil # for rcov
78
+ end
79
+
80
+ def find_by_id(what)
81
+ case what
82
+ when Regexp
83
+ elements_by_tag_names.find { |elem| elem.getId =~ what }
84
+ when String
85
+ obj = @object.getElementById(what)
86
+ return obj if @tags.include?(obj.getTagName)
87
+
88
+ $stderr.puts "warning: multiple elements with identical id? (#{what.inspect})" if $VERBOSE
89
+ elements_by_tag_names.find { |elem| elem.getId == what }
90
+ else
91
+ raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}"
92
+ end
93
+ end
94
+
95
+ def find_by_xpath(what)
96
+ what = ".#{what}" if what[0].chr == "/"
97
+ object = @object.getByXPath(what).to_a.first || return
98
+
99
+ return unless @idents.any? { |id| id.match?(object) }
100
+
101
+
102
+ object
103
+ end
104
+
105
+ def find_by_label(what)
106
+ obj = elements_by_tag_names(%w[label]).find { |e| Util.matches?(e.asText, what) }
107
+
108
+ return nil unless obj && (ref = obj.getReferencedElement)
109
+ return ref if @tags.include?(ref.getTagName)
110
+
111
+ find_by_id obj.getForAttribute
112
+ end
113
+
114
+ def elements_by_idents(idents = @idents)
115
+ get_by_idents(:select, idents)
116
+ end
117
+
118
+ def element_by_idents(idents = @idents)
119
+ get_by_idents(:find, idents)
120
+ end
121
+
122
+ private
123
+
124
+ def get_by_idents(meth, idents)
125
+ with_nullpointer_retry do
126
+ all_elements.send(meth) do |element|
127
+ next unless @tags.include?(element.getTagName)
128
+ idents.any? { |id| id.match?(element) }
129
+ end
130
+ end
131
+ end
132
+
133
+ def elements_by_tag_names(tags = @tags)
134
+ with_nullpointer_retry do
135
+ # HtmlUnit's getHtmlElementsByTagNames won't get elements in the correct
136
+ # order (making :index fail), so we're looping through all elements instead.
137
+ all_elements.select { |elem| tags.include?(elem.getTagName) }
138
+ end
139
+ end
140
+
141
+ def all_elements
142
+ unless @object
143
+ raise %{internal error in #{self.class}: @container=#{@container.inspect} @element_class=#{@element_class.inspect}
144
+ Please report this failure and the code/HTML that caused it at http://github.com/jarib/celerity/issues}
145
+ end
146
+
147
+ @object.getHtmlElementDescendants
148
+ end
149
+
150
+ # HtmlUnit throws NPEs sometimes when we're locating elements
151
+ # Retry seems to work fine.
152
+ def with_nullpointer_retry(max_retries = 3)
153
+ tries = 0
154
+ yield
155
+ rescue java.lang.NullPointerException => e
156
+ raise e if tries >= max_retries
157
+
158
+ tries += 1
159
+ warn "warning: celerity caught #{e} - retry ##{tries}"
160
+ retry
161
+ end
162
+
163
+ end # ElementLocator
164
+ end # Celerity
@@ -0,0 +1,54 @@
1
+ module Celerity
2
+
3
+ #
4
+ # Input: Button
5
+ #
6
+ # Class representing button elements.
7
+ #
8
+ # This class covers both <button> and <input type="submit|reset|image|button" /> elements.
9
+ #
10
+
11
+ class Button < InputElement
12
+ TAGS = [ Identifier.new('button'),
13
+ Identifier.new('input', :type => %w[submit reset image button]) ]
14
+
15
+ # Attribute list is a little weird due to this class covering both <button>
16
+ # and <input type="submit|reset|image|button" />
17
+ ATTRIBUTES = ATTRIBUTES | [
18
+ :accesskey,
19
+ :disabled,
20
+ :ismap,
21
+ :onblur,
22
+ :onfocus,
23
+ :src,
24
+ :tabindex,
25
+ :type,
26
+ :usemap,
27
+ ]
28
+ DEFAULT_HOW = :value
29
+
30
+ #
31
+ # @api private
32
+ #
33
+
34
+ def locate
35
+ # We want the :value attribute to point to the inner text for <button> elements,
36
+ # and to the value attribute for <input type="button"> elements.
37
+ if (val = @conditions[:value])
38
+ button_ident = Identifier.new('button')
39
+ button_ident.text = val
40
+ input_ident = Identifier.new('input', :type => %w[submit reset image button], :value => [val])
41
+
42
+ locator = ElementLocator.new(@container, self.class)
43
+ locator.idents = [button_ident, input_ident]
44
+
45
+ conditions = @conditions.dup
46
+ conditions.delete(:value)
47
+ @object = locator.find_by_conditions(conditions)
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ end # Button
54
+ end # Celerity
@@ -0,0 +1,29 @@
1
+ module Celerity
2
+
3
+ #
4
+ # For fields that accept file uploads
5
+ #
6
+
7
+ class FileField < InputElement
8
+ TAGS = [ Identifier.new('input', :type => %w[file]) ]
9
+ DEFAULT_HOW = :name
10
+
11
+ #
12
+ # Set the file field to the given path
13
+ #
14
+
15
+ def set(path)
16
+ assert_exists
17
+ path = path.to_s
18
+
19
+ @object.setValueAttribute path
20
+
21
+ unless @object.getContentType
22
+ @object.setContentType(Celerity::Util.content_type_for(path))
23
+ end
24
+
25
+ path
26
+ end
27
+
28
+ end # FileField
29
+ end # Celerity