celerity 0.0.6 → 0.0.7

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 (75) hide show
  1. data/History.txt +36 -0
  2. data/Manifest.txt +82 -0
  3. data/README.rdoc +78 -0
  4. data/Rakefile +25 -10
  5. data/celerity.gemspec +40 -0
  6. data/lib/celerity.rb +36 -20
  7. data/lib/celerity/browser.rb +396 -189
  8. data/lib/celerity/clickable_element.rb +25 -5
  9. data/lib/celerity/collections.rb +2 -2
  10. data/lib/celerity/container.rb +74 -61
  11. data/lib/celerity/default_viewer.rb +8 -4
  12. data/lib/celerity/disabled_element.rb +5 -5
  13. data/lib/celerity/element.rb +57 -35
  14. data/lib/celerity/element_collection.rb +22 -21
  15. data/lib/celerity/element_locator.rb +25 -18
  16. data/lib/celerity/elements/button.rb +13 -11
  17. data/lib/celerity/elements/file_field.rb +8 -4
  18. data/lib/celerity/elements/form.rb +7 -5
  19. data/lib/celerity/elements/frame.rb +6 -4
  20. data/lib/celerity/elements/image.rb +4 -4
  21. data/lib/celerity/elements/label.rb +1 -1
  22. data/lib/celerity/elements/link.rb +5 -5
  23. data/lib/celerity/elements/meta.rb +2 -1
  24. data/lib/celerity/elements/non_control_elements.rb +3 -3
  25. data/lib/celerity/elements/option.rb +7 -7
  26. data/lib/celerity/elements/radio_check.rb +18 -18
  27. data/lib/celerity/elements/select_list.rb +55 -25
  28. data/lib/celerity/elements/table.rb +21 -18
  29. data/lib/celerity/elements/table_cell.rb +1 -1
  30. data/lib/celerity/elements/table_elements.rb +1 -1
  31. data/lib/celerity/elements/table_row.rb +5 -5
  32. data/lib/celerity/elements/text_field.rb +33 -28
  33. data/lib/celerity/exception.rb +11 -5
  34. data/lib/celerity/htmlunit.rb +24 -8
  35. data/lib/celerity/htmlunit/commons-codec-1.4.jar +0 -0
  36. data/lib/celerity/htmlunit/htmlunit-2.6.jar +0 -0
  37. data/lib/celerity/htmlunit/htmlunit-core-js-2.6.jar +0 -0
  38. data/lib/celerity/htmlunit/nekohtml-1.9.13.jar +0 -0
  39. data/lib/celerity/htmlunit/{xercesImpl-2.8.1.jar → xercesImpl-2.9.1.jar} +0 -0
  40. data/lib/celerity/ignoring_web_connection.rb +15 -0
  41. data/lib/celerity/input_element.rb +1 -1
  42. data/lib/celerity/listener.rb +23 -17
  43. data/lib/celerity/short_inspect.rb +20 -0
  44. data/lib/celerity/util.rb +5 -2
  45. data/lib/celerity/version.rb +3 -10
  46. data/lib/celerity/viewer_connection.rb +89 -0
  47. data/lib/celerity/watir_compatibility.rb +2 -5
  48. data/lib/celerity/xpath_support.rb +48 -0
  49. data/spec/browser_authentication_spec.rb +16 -0
  50. data/spec/browser_spec.rb +300 -0
  51. data/spec/clickable_element_spec.rb +39 -0
  52. data/spec/default_viewer_spec.rb +23 -0
  53. data/spec/element_spec.rb +51 -0
  54. data/spec/filefield_spec.rb +18 -0
  55. data/spec/htmlunit_spec.rb +56 -0
  56. data/spec/index_offset_spec.rb +24 -0
  57. data/spec/listener_spec.rb +142 -0
  58. data/spec/spec_helper.rb +8 -0
  59. data/tasks/benchmark.rake +4 -0
  60. data/tasks/deployment.rake +43 -0
  61. data/tasks/environment.rake +7 -0
  62. data/tasks/fix.rake +25 -0
  63. data/tasks/jar.rake +4 -6
  64. data/tasks/rspec.rake +43 -0
  65. data/tasks/simple_ci.rake +94 -0
  66. data/tasks/snapshot.rake +22 -0
  67. data/tasks/website.rake +17 -0
  68. data/tasks/yard.rake +9 -0
  69. metadata +59 -26
  70. data/README.txt +0 -69
  71. data/lib/celerity/extra/method_generator.rb +0 -170
  72. data/lib/celerity/htmlunit/commons-codec-1.3.jar +0 -0
  73. data/lib/celerity/htmlunit/htmlunit-2.5-SNAPSHOT.jar +0 -0
  74. data/lib/celerity/htmlunit/htmlunit-core-js-2.5-SNAPSHOT.jar +0 -0
  75. data/lib/celerity/htmlunit/nekohtml-1.9.12-20090308.130127-11.jar +0 -0
@@ -1,14 +1,14 @@
1
1
  module Celerity
2
-
2
+
3
3
  #
4
4
  # Superclass for all HTML elements.
5
- #
6
-
5
+ #
6
+
7
7
  class Element
8
8
  include Exception
9
9
  include Container
10
10
 
11
- attr_reader :container, :object
11
+ attr_reader :container
12
12
 
13
13
  # number of spaces that separate the property from the value in the create_string method
14
14
  TO_S_SIZE = 14
@@ -52,12 +52,13 @@ module Celerity
52
52
  end
53
53
 
54
54
  @conditions.freeze
55
+ @object = nil
55
56
  end
56
57
 
57
58
  #
58
59
  # Get the parent element
59
60
  # @return [Celerity::Element, nil] subclass of Celerity::Element, or nil if no parent was found
60
- #
61
+ #
61
62
 
62
63
  def parent
63
64
  assert_exists
@@ -73,22 +74,50 @@ module Celerity
73
74
 
74
75
  #
75
76
  # Sets the focus to this element.
76
- #
77
+ #
77
78
 
78
79
  def focus
79
80
  assert_exists
80
81
  @object.focus
81
82
  end
82
83
 
84
+ #
85
+ # Fires the given event for this element
86
+ #
87
+
88
+ def fire_event(event_name)
89
+ assert_exists
90
+ @object.fireEvent(event_name.sub(/^on/, ''))
91
+ end
92
+
83
93
  #
84
94
  # Used internally. Find the element on the page.
85
95
  # @api private
86
- #
96
+ #
87
97
 
88
98
  def locate
89
99
  @object = ElementLocator.new(@container, self.class).find_by_conditions(@conditions)
90
100
  end
91
101
 
102
+ #
103
+ # Returns the HtmlUnit object backing this element
104
+ #
105
+
106
+ def object
107
+ @object || locate
108
+ end
109
+
110
+ #
111
+ # Returns a JavaScript object representing the receiver
112
+ #
113
+ # @api internal - subject to change
114
+ #
115
+
116
+ def javascript_object
117
+ assert_exists
118
+ @object.getScriptObject
119
+ end
120
+
92
121
  #
93
122
  # @return [String] A string representation of the element.
94
123
  #
@@ -101,7 +130,7 @@ module Celerity
101
130
  #
102
131
  # @param [String, #to_s] The attribute.
103
132
  # @return [String] The value of the given attribute.
104
- #
133
+ #
105
134
 
106
135
  def attribute_value(attribute)
107
136
  assert_exists
@@ -114,17 +143,11 @@ module Celerity
114
143
  # its parents) into account - styles from applied CSS is not considered.
115
144
  #
116
145
  # @return [boolean]
117
- #
146
+ #
118
147
 
119
148
  def visible?
120
- obj = self
121
- while obj
122
- return false if obj.respond_to?(:type) && obj.type == 'hidden'
123
- return false if obj.style =~ /display\s*:\s*none|visibility\s*:\s*hidden/
124
- obj = obj.parent
125
- end
126
-
127
- return true
149
+ assert_exists
150
+ @object.isDisplayed
128
151
  end
129
152
 
130
153
  #
@@ -132,19 +155,19 @@ module Celerity
132
155
  #
133
156
  # @raise [Celerity::Exception::UnknownObjectException] if the element can't be found.
134
157
  # @api private
135
- #
158
+ #
136
159
 
137
160
  def assert_exists
138
- locate
161
+ locate unless @object
139
162
  unless @object
140
163
  raise UnknownObjectException, "Unable to locate #{self.class.name[/::(.*)$/, 1]}, using #{identifier_string}"
141
164
  end
142
165
  end
143
-
166
+
144
167
  #
145
168
  # Checks if the element exists.
146
169
  # @return [true, false]
147
- #
170
+ #
148
171
 
149
172
  def exists?
150
173
  assert_exists
@@ -153,13 +176,13 @@ module Celerity
153
176
  false
154
177
  end
155
178
  alias_method :exist?, :exists?
156
-
179
+
157
180
  #
158
181
  # Return a text representation of the element as it would appear in a browser.
159
182
  #
160
183
  # @see inner_text
161
184
  # @return [String]
162
- #
185
+ #
163
186
 
164
187
  def text
165
188
  assert_exists
@@ -173,7 +196,7 @@ module Celerity
173
196
  #
174
197
  # @see text
175
198
  # @return [String]
176
- #
199
+ #
177
200
 
178
201
  def inner_text
179
202
  assert_exists
@@ -182,7 +205,7 @@ module Celerity
182
205
 
183
206
  #
184
207
  # @return [String] The normative XML representation of the element (including children).
185
- #
208
+ #
186
209
 
187
210
  def to_xml
188
211
  assert_exists
@@ -194,14 +217,14 @@ module Celerity
194
217
 
195
218
  #
196
219
  # @return [String] A string representation of the element's attributes.
197
- #
220
+ #
198
221
 
199
222
  def attribute_string
200
223
  assert_exists
201
224
 
202
225
  result = ''
203
226
  @object.getAttributes.each do |attribute|
204
- result << %Q{#{attribute.getName}="#{attribute.getHtmlValue}"}
227
+ result << %Q{#{attribute.getName}="#{attribute.getValue}"}
205
228
  end
206
229
 
207
230
  result
@@ -209,7 +232,7 @@ module Celerity
209
232
 
210
233
  #
211
234
  # return the canonical xpath for this element (Celerity-specific)
212
- #
235
+ #
213
236
 
214
237
  def xpath
215
238
  assert_exists
@@ -223,23 +246,22 @@ module Celerity
223
246
  #
224
247
  # @return [String] The resulting attribute.
225
248
  # @raise [NoMethodError] if the element doesn't support this attribute.
226
- #
249
+ #
227
250
 
228
251
  def method_missing(meth, *args, &blk)
229
252
  assert_exists
230
253
 
231
254
  meth = selector_to_attribute(meth)
232
255
 
233
- if self.class::ATTRIBUTES.include?(meth)
234
- return @object.getAttributeValue(meth.to_s)
256
+ if self.class::ATTRIBUTES.include?(meth) || (self.class == Element && @object.hasAttribute(meth.to_s))
257
+ return @object.getAttribute(meth.to_s)
235
258
  end
236
-
237
259
  Log.warn "Element\#method_missing calling super with #{meth.inspect}"
238
260
  super
239
261
  end
240
-
262
+
241
263
  def methods(*args)
242
- ms = super
264
+ ms = super
243
265
  ms += self.class::ATTRIBUTES.map { |e| e.to_s }
244
266
  ms.sort
245
267
  end
@@ -260,7 +282,7 @@ module Celerity
260
282
  end
261
283
 
262
284
  element.getAttributes.each do |attribute|
263
- ret << " #{attribute.getName}:".ljust(TO_S_SIZE+2) + attribute.getHtmlValue.to_s
285
+ ret << " #{attribute.getName}:".ljust(TO_S_SIZE+2) + attribute.getValue.to_s
264
286
  end
265
287
 
266
288
  unless (text = element.asText).empty?
@@ -1,13 +1,13 @@
1
1
  module Celerity
2
-
2
+
3
3
  #
4
4
  # This class is the superclass for the iterator classes (Buttons, Links, Spans etc.)
5
5
  # It would normally only be accessed by the iterator methods (Browser#spans, Browser#links, ...).
6
- #
7
-
6
+ #
7
+
8
8
  class ElementCollection
9
9
  include Enumerable
10
-
10
+
11
11
  #
12
12
  # @api private
13
13
  #
@@ -17,10 +17,10 @@ module Celerity
17
17
  @object = (how == :object ? what : nil)
18
18
  @length = length
19
19
  end
20
-
20
+
21
21
  #
22
22
  # @return [Fixnum] The number of elements in this collection.
23
- #
23
+ #
24
24
 
25
25
  def length
26
26
  if @object
@@ -45,22 +45,23 @@ module Celerity
45
45
 
46
46
  @length
47
47
  end
48
-
48
+
49
49
  #
50
50
  # Get the element at the given index.
51
- # This is 1-indexed to keep compatibility with Watir - subject to change.
51
+ # By default, this is 1-indexed to keep compatibility with Watir.
52
+ #
52
53
  # Also note that because of Watir's lazy loading, this will return an Element
53
54
  # instance even if the index is out of bounds.
54
55
  #
55
- # @param [Fixnum] n Index of wanted element, 1-indexed.
56
+ # @param [Fixnum] n Index of wanted element, 1-indexed unless Celerity.index_offset is changed.
56
57
  # @return [Celerity::Element] Returns a subclass of Celerity::Element
57
- #
58
+ #
58
59
 
59
60
  def [](n)
60
- if @elements && @elements[n - INDEX_OFFSET]
61
- element_class.new(@container, :object, @elements[n - INDEX_OFFSET])
61
+ if @elements && @elements[n - Celerity.index_offset]
62
+ element_class.new(@container, :object, @elements[n - Celerity.index_offset])
62
63
  else
63
- iterator_object(n - INDEX_OFFSET)
64
+ iterator_object(n - Celerity.index_offset)
64
65
  end
65
66
  end
66
67
 
@@ -68,20 +69,20 @@ module Celerity
68
69
  # Get the first element in this collection. (Celerity-specific)
69
70
  #
70
71
  # @return [Celerity::Element] Returns a subclass of Celerity::Element
71
- #
72
-
72
+ #
73
+
73
74
  def first
74
- self[INDEX_OFFSET]
75
+ self[Celerity.index_offset]
75
76
  end
76
77
 
77
78
  #
78
79
  # Get the last element in this collection. (Celerity-specific)
79
80
  #
80
81
  # @return [Celerity::Element] Returns a subclass of Celerity::Element
81
- #
82
-
82
+ #
83
+
83
84
  def last
84
- self[INDEX_OFFSET - 1]
85
+ self[Celerity.index_offset - 1]
85
86
  end
86
87
 
87
88
  #
@@ -90,8 +91,8 @@ module Celerity
90
91
  # puts browser.text_fields
91
92
  #
92
93
  # @return [String] A string representation of all elements in this collection.
93
- #
94
-
94
+ #
95
+
95
96
  def to_s
96
97
  map { |e| e.to_s }.join("\n")
97
98
  end
@@ -2,8 +2,8 @@ module Celerity
2
2
 
3
3
  #
4
4
  # Used internally to locate elements on the page.
5
- #
6
-
5
+ #
6
+
7
7
  class ElementLocator
8
8
  include Celerity::Exception
9
9
  attr_accessor :idents
@@ -35,8 +35,6 @@ module Celerity
35
35
  raise ArgumentError, "expected an HtmlUnit::Html::HtmlElement subclass, got #{what.inspect}:#{what.class}"
36
36
  end
37
37
  return what
38
- when :id
39
- return find_by_id(what)
40
38
  when :xpath
41
39
  return find_by_xpath(what)
42
40
  when :label
@@ -49,10 +47,12 @@ module Celerity
49
47
  how = :text
50
48
  end
51
49
 
52
- if @attributes.include?(how = how.to_sym)
50
+ if how == :id && conditions.size == 1
51
+ return find_by_id(what)
52
+ elsif @attributes.include?(how = how.to_sym)
53
53
  attributes[how] << what
54
54
  elsif how == :index
55
- index = what.to_i - INDEX_OFFSET
55
+ index = what.to_i - Celerity.index_offset
56
56
  elsif how == :text
57
57
  text = what
58
58
  else
@@ -82,7 +82,7 @@ module Celerity
82
82
  when Regexp
83
83
  elements_by_tag_names.find { |elem| elem.getId =~ what }
84
84
  when String
85
- obj = @object.getHtmlElementById(what)
85
+ obj = @object.getElementById(what)
86
86
  return obj if @tags.include?(obj.getTagName)
87
87
 
88
88
  $stderr.puts "warning: multiple elements with identical id? (#{what.inspect})" if $VERBOSE
@@ -106,19 +106,19 @@ module Celerity
106
106
  find_by_id obj.getForAttribute
107
107
  end
108
108
 
109
- def elements_by_idents(idents = nil)
110
- get_by_idents(:select, idents || @idents)
109
+ def elements_by_idents(idents = @idents)
110
+ get_by_idents(:select, idents)
111
111
  end
112
112
 
113
- def element_by_idents(idents = nil)
114
- get_by_idents(:find, idents || @idents)
113
+ def element_by_idents(idents = @idents)
114
+ get_by_idents(:find, idents)
115
115
  end
116
116
 
117
117
  private
118
118
 
119
119
  def get_by_idents(meth, idents)
120
120
  with_nullpointer_retry do
121
- @object.getAllHtmlChildElements.send(meth) do |e|
121
+ all_elements.send(meth) do |e|
122
122
  next unless @tags.include?(e.getTagName)
123
123
  idents.any? { |id| element_matches_ident?(e, id) }
124
124
  end
@@ -129,7 +129,7 @@ module Celerity
129
129
  return false unless ident.tag == element.getTagName
130
130
 
131
131
  attr_result = ident.attributes.all? do |key, values|
132
- values.any? { |val| matches?(element.getAttributeValue(key.to_s), val) }
132
+ values.any? { |val| matches?(element.getAttribute(key.to_s), val) }
133
133
  end
134
134
 
135
135
  if ident.text
@@ -142,13 +142,20 @@ module Celerity
142
142
  def elements_by_tag_names(tags = @tags)
143
143
  with_nullpointer_retry do
144
144
  # HtmlUnit's getHtmlElementsByTagNames won't get elements in the correct
145
- # order (making :index fail), so we're using getAllHtmlChildElements instead.
146
- @object.getAllHtmlChildElements.select do |elem|
147
- tags.include?(elem.getTagName)
148
- end
145
+ # order (making :index fail), so we're looping through all elements instead.
146
+ all_elements.select { |elem| tags.include?(elem.getTagName) }
149
147
  end
150
148
  end
151
149
 
150
+ def all_elements
151
+ unless @object
152
+ raise %{internal error in #{self.class}: @container=#{@container.inspect} @element_class=#{@element_class.inspect}
153
+ Please report this failure and the code/HTML that caused it at http://github.com/jarib/celerity/issues}
154
+ end
155
+
156
+ @object.getAllHtmlChildElements
157
+ end
158
+
152
159
  # HtmlUnit throws NPEs sometimes when we're locating elements
153
160
  # Retry seems to work fine.
154
161
  def with_nullpointer_retry(max_retries = 3)
@@ -158,7 +165,7 @@ module Celerity
158
165
  raise e if tries >= max_retries
159
166
 
160
167
  tries += 1
161
- $stderr.puts "warning: celerity caught #{e} - retry ##{tries}"
168
+ warn "warning: celerity caught #{e} - retry ##{tries}"
162
169
  retry
163
170
  end
164
171
 
@@ -1,36 +1,38 @@
1
1
  module Celerity
2
-
2
+
3
3
  #
4
4
  # Input: Button
5
5
  #
6
- # Class representing button elements
6
+ # Class representing button elements.
7
+ #
8
+ # This class covers both <button> and <input type="submit|reset|image|button" /> elements.
7
9
  #
8
-
10
+
9
11
  class Button < InputElement
10
12
  TAGS = [ Identifier.new('button'),
11
13
  Identifier.new('input', :type => %w[submit reset image button]) ]
12
-
14
+
13
15
  # Attribute list is a little weird due to this class covering both <button>
14
16
  # and <input type="submit|reset|image|button" />
15
- ATTRIBUTES = BASE_ATTRIBUTES | [:type, :disabled, :tabindex, :accesskey, :onfocus, :onblur] | [:src, :usemap, :ismap]
17
+ ATTRIBUTES = ATTRIBUTES | [:type, :disabled, :tabindex, :accesskey, :onfocus, :onblur] | [:src, :usemap, :ismap]
16
18
  DEFAULT_HOW = :value
17
19
 
18
20
  #
19
21
  # @api private
20
22
  #
21
-
23
+
22
24
  def locate
23
- # We want the :value attribute to point to the inner HTML for <button> elements,
24
- # and to the value attribute for <input type="button"> elements.
25
-
25
+ # We want the :value attribute to point to the inner HTML for <button> elements,
26
+ # and to the value attribute for <input type="button"> elements.
27
+
26
28
  if (val = @conditions[:value])
27
29
  button_ident = Identifier.new('button')
28
30
  button_ident.text = val
29
31
  input_ident = Identifier.new('input', :type => %w[submit reset image button], :value => [val])
30
-
32
+
31
33
  locator = ElementLocator.new(@container, self.class)
32
34
  locator.idents = [button_ident, input_ident]
33
-
35
+
34
36
  conditions = @conditions.dup
35
37
  conditions.delete(:value)
36
38
  @object = locator.find_by_conditions(conditions)