celerity 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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)