celerity_thingista 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +9 -0
- data/.gitmodules +3 -0
- data/.travis.yml +4 -0
- data/Gemfile +3 -0
- data/LICENSE +278 -0
- data/README.rdoc +84 -0
- data/Rakefile +15 -0
- data/benchmark/bm_2000_spans.rb +48 -0
- data/benchmark/bm_digg.rb +26 -0
- data/benchmark/bm_google_images.rb +36 -0
- data/benchmark/bm_input_locator.rb +69 -0
- data/benchmark/bm_text_input.rb +19 -0
- data/benchmark/loader.rb +14 -0
- data/celerity.gemspec +26 -0
- data/lib/celerity.rb +75 -0
- data/lib/celerity/browser.rb +924 -0
- data/lib/celerity/clickable_element.rb +73 -0
- data/lib/celerity/collections.rb +164 -0
- data/lib/celerity/container.rb +802 -0
- data/lib/celerity/default_viewer.rb +14 -0
- data/lib/celerity/disabled_element.rb +40 -0
- data/lib/celerity/element.rb +314 -0
- data/lib/celerity/element_collection.rb +115 -0
- data/lib/celerity/element_locator.rb +164 -0
- data/lib/celerity/elements/button.rb +54 -0
- data/lib/celerity/elements/file_field.rb +29 -0
- data/lib/celerity/elements/form.rb +22 -0
- data/lib/celerity/elements/frame.rb +86 -0
- data/lib/celerity/elements/image.rb +89 -0
- data/lib/celerity/elements/label.rb +16 -0
- data/lib/celerity/elements/link.rb +43 -0
- data/lib/celerity/elements/meta.rb +14 -0
- data/lib/celerity/elements/non_control_elements.rb +124 -0
- data/lib/celerity/elements/option.rb +38 -0
- data/lib/celerity/elements/radio_check.rb +114 -0
- data/lib/celerity/elements/select_list.rb +146 -0
- data/lib/celerity/elements/table.rb +154 -0
- data/lib/celerity/elements/table_cell.rb +36 -0
- data/lib/celerity/elements/table_elements.rb +42 -0
- data/lib/celerity/elements/table_row.rb +54 -0
- data/lib/celerity/elements/text_field.rb +168 -0
- data/lib/celerity/exception.rb +83 -0
- data/lib/celerity/htmlunit.rb +64 -0
- data/lib/celerity/htmlunit/commons-codec-1.7.jar +0 -0
- data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
- data/lib/celerity/htmlunit/commons-io-2.4.jar +0 -0
- data/lib/celerity/htmlunit/commons-lang3-3.1.jar +0 -0
- data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
- data/lib/celerity/htmlunit/cssparser-0.9.9.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-2.12.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-core-js-2.12.jar +0 -0
- data/lib/celerity/htmlunit/httpclient-4.2.3.jar +0 -0
- data/lib/celerity/htmlunit/httpcore-4.2.2.jar +0 -0
- data/lib/celerity/htmlunit/httpmime-4.2.3.jar +0 -0
- data/lib/celerity/htmlunit/jetty-http-8.1.9.v20130131.jar +0 -0
- data/lib/celerity/htmlunit/jetty-io-8.1.9.v20130131.jar +0 -0
- data/lib/celerity/htmlunit/jetty-util-8.1.9.v20130131.jar +0 -0
- data/lib/celerity/htmlunit/jetty-websocket-8.1.9.v20130131.jar +0 -0
- data/lib/celerity/htmlunit/nekohtml-1.9.18.jar +0 -0
- data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
- data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
- data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
- data/lib/celerity/htmlunit/xercesImpl-2.10.0.jar +0 -0
- data/lib/celerity/htmlunit/xml-apis-1.4.01.jar +0 -0
- data/lib/celerity/identifier.rb +28 -0
- data/lib/celerity/ignoring_web_connection.rb +15 -0
- data/lib/celerity/input_element.rb +25 -0
- data/lib/celerity/javascript_debugger.rb +32 -0
- data/lib/celerity/listener.rb +143 -0
- data/lib/celerity/resources/no_viewer.png +0 -0
- data/lib/celerity/short_inspect.rb +29 -0
- data/lib/celerity/util.rb +129 -0
- data/lib/celerity/version.rb +3 -0
- data/lib/celerity/viewer_connection.rb +89 -0
- data/lib/celerity/watir_compatibility.rb +70 -0
- data/lib/celerity/xpath_support.rb +50 -0
- data/spec/browser_authentication_spec.rb +16 -0
- data/spec/browser_spec.rb +439 -0
- data/spec/button_spec.rb +24 -0
- data/spec/clickable_element_spec.rb +39 -0
- data/spec/default_viewer_spec.rb +23 -0
- data/spec/element_spec.rb +77 -0
- data/spec/filefield_spec.rb +18 -0
- data/spec/htmlunit_spec.rb +63 -0
- data/spec/implementation.rb +7 -0
- data/spec/index_offset_spec.rb +24 -0
- data/spec/link_spec.rb +16 -0
- data/spec/listener_spec.rb +142 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/table_spec.rb +41 -0
- data/spec/watir_compatibility_spec.rb +32 -0
- data/tasks/benchmark.rake +4 -0
- data/tasks/check.rake +24 -0
- data/tasks/clean.rake +3 -0
- data/tasks/fix.rake +25 -0
- data/tasks/jar.rake +55 -0
- data/tasks/rdoc.rake +4 -0
- data/tasks/snapshot.rake +25 -0
- data/tasks/spec.rake +27 -0
- data/tasks/website.rake +10 -0
- data/tasks/yard.rake +16 -0
- data/website/benchmarks.html +237 -0
- data/website/css/color.css +153 -0
- data/website/css/hacks.css +3 -0
- data/website/css/layout.css +179 -0
- data/website/css/screen.css +5 -0
- data/website/css/textmate.css +226 -0
- data/website/css/typography.css +72 -0
- data/website/gfx/body_bg.gif +0 -0
- data/website/gfx/button_bg.jpg +0 -0
- data/website/gfx/header_bg.jpg +0 -0
- data/website/gfx/header_left.jpg +0 -0
- data/website/gfx/header_right.jpg +0 -0
- data/website/gfx/nav_bg.jpg +0 -0
- data/website/index.html +125 -0
- data/website/yard/index.html +1 -0
- metadata +246 -0
@@ -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,314 @@
|
|
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
|
+
attr_writer :identifier_string
|
13
|
+
|
14
|
+
# HTML 4.01 Transitional DTD
|
15
|
+
HTML_401_TRANSITIONAL = {
|
16
|
+
:core => [:class, :id, :style, :title],
|
17
|
+
:cell_halign => [:align, :char, :charoff],
|
18
|
+
:cell_valign => [:valign],
|
19
|
+
:i18n => [:dir, :lang],
|
20
|
+
:event => [:onclick, :ondblclick, :onmousedown, :onmouseup, :onmouseover,
|
21
|
+
:onmousemove, :onmouseout, :onkeypress, :onkeydown, :onkeyup],
|
22
|
+
:sloppy => [:name, :value]
|
23
|
+
}
|
24
|
+
|
25
|
+
CELLHALIGN_ATTRIBUTES = HTML_401_TRANSITIONAL[:cell_halign]
|
26
|
+
CELLVALIGN_ATTRIBUTES = HTML_401_TRANSITIONAL[:cell_valign]
|
27
|
+
BASE_ATTRIBUTES = HTML_401_TRANSITIONAL.values_at(:core, :i18n, :event, :sloppy).flatten
|
28
|
+
ATTRIBUTES = BASE_ATTRIBUTES
|
29
|
+
TAGS = []
|
30
|
+
|
31
|
+
DEFAULT_HOW = nil
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def initialize(container, *args)
|
35
|
+
self.container = container
|
36
|
+
|
37
|
+
case args.size
|
38
|
+
when 2
|
39
|
+
@conditions = { args[0] => args[1] }
|
40
|
+
when 1
|
41
|
+
if args.first.is_a? Hash
|
42
|
+
@conditions = args.first
|
43
|
+
elsif (how = self.class::DEFAULT_HOW)
|
44
|
+
@conditions = { how => args.first }
|
45
|
+
else
|
46
|
+
raise ArgumentError, "wrong number of arguments (1 for 2)"
|
47
|
+
end
|
48
|
+
when 0
|
49
|
+
@conditions = { :index => Celerity.index_offset }
|
50
|
+
else
|
51
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
|
52
|
+
end
|
53
|
+
|
54
|
+
@conditions.freeze
|
55
|
+
@object = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
return false unless other.kind_of? Element
|
60
|
+
xpath == other.xpath
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Get the parent element
|
65
|
+
# @return [Celerity::Element, nil] subclass of Celerity::Element, or nil if no parent was found
|
66
|
+
#
|
67
|
+
|
68
|
+
def parent
|
69
|
+
assert_exists
|
70
|
+
|
71
|
+
obj = @object.parentNode
|
72
|
+
until element_class = Celerity::Util.htmlunit2celerity(obj.class)
|
73
|
+
return nil if obj.nil?
|
74
|
+
obj = obj.parentNode
|
75
|
+
end
|
76
|
+
|
77
|
+
element_class.new(@container, :object, obj)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Sets the focus to this element.
|
82
|
+
#
|
83
|
+
|
84
|
+
def focus
|
85
|
+
assert_exists
|
86
|
+
@object.focus
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Returns true if this element is the currently focused element
|
91
|
+
#
|
92
|
+
# Celerity-specific.
|
93
|
+
#
|
94
|
+
|
95
|
+
def focused?
|
96
|
+
assert_exists
|
97
|
+
@object == browser.page.getFocusedElement
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Fires the given event for this element
|
102
|
+
#
|
103
|
+
|
104
|
+
def fire_event(event_name)
|
105
|
+
assert_exists
|
106
|
+
@object.fireEvent(event_name.sub(/^on/, ''))
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Used internally. Find the element on the page.
|
111
|
+
# @api private
|
112
|
+
#
|
113
|
+
|
114
|
+
def locate
|
115
|
+
@object = ElementLocator.new(@container, self.class).find_by_conditions(@conditions)
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Returns the HtmlUnit object backing this element
|
120
|
+
#
|
121
|
+
|
122
|
+
def object
|
123
|
+
@object || locate
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Returns a JavaScript object representing the receiver
|
128
|
+
#
|
129
|
+
# @api internal - subject to change
|
130
|
+
#
|
131
|
+
|
132
|
+
def javascript_object
|
133
|
+
assert_exists
|
134
|
+
@object.getScriptObject
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# @return [String] A string representation of the element.
|
139
|
+
#
|
140
|
+
|
141
|
+
def to_s
|
142
|
+
assert_exists
|
143
|
+
Celerity::Util.create_string @object
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# @param [String, #to_s] The attribute.
|
148
|
+
# @return [String] The value of the given attribute.
|
149
|
+
#
|
150
|
+
|
151
|
+
def attribute_value(attribute)
|
152
|
+
assert_exists
|
153
|
+
@object.getAttribute attribute.to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Check if the element is visible to the user or not.
|
158
|
+
# Note that this only takes the _style attribute_ of the element (and
|
159
|
+
# its parents) into account - styles from applied CSS is not considered.
|
160
|
+
#
|
161
|
+
# @return [boolean]
|
162
|
+
#
|
163
|
+
|
164
|
+
def visible?
|
165
|
+
assert_exists
|
166
|
+
@object.isDisplayed
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Used internally to ensure the element actually exists.
|
171
|
+
#
|
172
|
+
# @raise [Celerity::Exception::UnknownObjectException] if the element can't be found.
|
173
|
+
# @api private
|
174
|
+
#
|
175
|
+
|
176
|
+
def assert_exists
|
177
|
+
locate unless @object
|
178
|
+
unless @object
|
179
|
+
raise UnknownObjectException, "Unable to locate #{self.class.name[/::(.*)$/, 1]}, using #{identifier_string}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# Checks if the element exists.
|
185
|
+
# @return [true, false]
|
186
|
+
#
|
187
|
+
|
188
|
+
def exists?
|
189
|
+
assert_exists
|
190
|
+
true
|
191
|
+
rescue UnknownObjectException, UnknownFrameException
|
192
|
+
false
|
193
|
+
end
|
194
|
+
alias_method :exist?, :exists?
|
195
|
+
|
196
|
+
#
|
197
|
+
# Return a text representation of the element as it would appear in a browser.
|
198
|
+
#
|
199
|
+
# @see inner_text
|
200
|
+
# @return [String]
|
201
|
+
#
|
202
|
+
|
203
|
+
def text
|
204
|
+
assert_exists
|
205
|
+
@object.asText.strip # this must behave like ElementLocator
|
206
|
+
end
|
207
|
+
|
208
|
+
#
|
209
|
+
# Return the text content of this DOM node, disregarding its visibility.
|
210
|
+
#
|
211
|
+
# (Celerity-specific?)
|
212
|
+
#
|
213
|
+
# @see text
|
214
|
+
# @return [String]
|
215
|
+
#
|
216
|
+
|
217
|
+
def inner_text
|
218
|
+
assert_exists
|
219
|
+
Celerity::Util.normalize_text @object.getTextContent
|
220
|
+
end
|
221
|
+
|
222
|
+
#
|
223
|
+
# @return [String] The normative XML representation of the element (including children).
|
224
|
+
#
|
225
|
+
|
226
|
+
def to_xml
|
227
|
+
assert_exists
|
228
|
+
@object.asXml
|
229
|
+
end
|
230
|
+
alias_method :asXml, :to_xml
|
231
|
+
alias_method :as_xml, :to_xml
|
232
|
+
alias_method :html, :to_xml
|
233
|
+
|
234
|
+
#
|
235
|
+
# @return [String] A string representation of the element's attributes.
|
236
|
+
#
|
237
|
+
|
238
|
+
def attribute_string
|
239
|
+
assert_exists
|
240
|
+
|
241
|
+
result = ''
|
242
|
+
@object.getAttributes.each do |attribute|
|
243
|
+
result << %Q{#{attribute.getName}="#{attribute.getValue}"}
|
244
|
+
end
|
245
|
+
|
246
|
+
result
|
247
|
+
end
|
248
|
+
|
249
|
+
#
|
250
|
+
# return the canonical xpath for this element (Celerity-specific)
|
251
|
+
#
|
252
|
+
|
253
|
+
def xpath
|
254
|
+
assert_exists
|
255
|
+
@object.getCanonicalXPath
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# Dynamically get element attributes.
|
260
|
+
#
|
261
|
+
# @see ATTRIBUTES constant for a list of valid methods for a given element.
|
262
|
+
#
|
263
|
+
# @return [String] The resulting attribute.
|
264
|
+
# @raise [NoMethodError] if the element doesn't support this attribute.
|
265
|
+
#
|
266
|
+
|
267
|
+
def method_missing(meth, *args, &blk)
|
268
|
+
assert_exists
|
269
|
+
|
270
|
+
meth = selector_to_attribute(meth)
|
271
|
+
|
272
|
+
if self.class::ATTRIBUTES.include?(meth) || (self.class == Element && @object.hasAttribute(meth.to_s))
|
273
|
+
return @object.getAttribute(meth.to_s)
|
274
|
+
end
|
275
|
+
Log.warn "Element\#method_missing calling super with #{meth.inspect}"
|
276
|
+
super
|
277
|
+
end
|
278
|
+
|
279
|
+
def methods(*args)
|
280
|
+
ms = super
|
281
|
+
ms += self.class::ATTRIBUTES.map { |e| e.to_s }
|
282
|
+
ms.sort
|
283
|
+
end
|
284
|
+
|
285
|
+
def respond_to?(meth, include_private = false)
|
286
|
+
meth = selector_to_attribute(meth)
|
287
|
+
return true if self.class::ATTRIBUTES.include?(meth)
|
288
|
+
super
|
289
|
+
end
|
290
|
+
|
291
|
+
private
|
292
|
+
|
293
|
+
def identifier_string
|
294
|
+
@identifier_string ||= (
|
295
|
+
if @conditions.size == 1
|
296
|
+
how, what = @conditions.to_a.first
|
297
|
+
"#{how.inspect} and #{what.inspect}"
|
298
|
+
else
|
299
|
+
@conditions.inspect
|
300
|
+
end
|
301
|
+
)
|
302
|
+
end
|
303
|
+
|
304
|
+
def selector_to_attribute(meth)
|
305
|
+
case meth
|
306
|
+
when :class_name then :class
|
307
|
+
when :caption then :value
|
308
|
+
when :url then :href
|
309
|
+
else meth
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
end # Element
|
314
|
+
end # Celerity
|
@@ -0,0 +1,115 @@
|
|
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
|
+
# Returns true if the collection is empty.
|
37
|
+
#
|
38
|
+
|
39
|
+
def empty?
|
40
|
+
length == 0
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# @yieldparam [Celerity::Element] element Iterate through the elements in this collection.
|
45
|
+
#
|
46
|
+
|
47
|
+
def each
|
48
|
+
if @elements
|
49
|
+
@elements.each { |e| yield(element_class.new(@container, :object, e)) }
|
50
|
+
else
|
51
|
+
0.upto(@length - 1) { |i| yield iterator_object(i) }
|
52
|
+
end
|
53
|
+
|
54
|
+
@length
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Get the element at the given index.
|
59
|
+
# By default, this is 1-indexed to keep compatibility with Watir.
|
60
|
+
#
|
61
|
+
# Also note that because of Watir's lazy loading, this will return an Element
|
62
|
+
# instance even if the index is out of bounds.
|
63
|
+
#
|
64
|
+
# @param [Fixnum] n Index of wanted element, 1-indexed unless Celerity.index_offset is changed.
|
65
|
+
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
66
|
+
#
|
67
|
+
|
68
|
+
def [](n)
|
69
|
+
if @elements && @elements[n - Celerity.index_offset]
|
70
|
+
element_class.new(@container, :object, @elements[n - Celerity.index_offset])
|
71
|
+
else
|
72
|
+
iterator_object(n - Celerity.index_offset)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Get the first element in this collection. (Celerity-specific)
|
78
|
+
#
|
79
|
+
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
80
|
+
#
|
81
|
+
|
82
|
+
def first
|
83
|
+
self[Celerity.index_offset]
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Get the last element in this collection. (Celerity-specific)
|
88
|
+
#
|
89
|
+
# @return [Celerity::Element] Returns a subclass of Celerity::Element
|
90
|
+
#
|
91
|
+
|
92
|
+
def last
|
93
|
+
self[Celerity.index_offset - 1]
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Note: This can be quite useful in irb:
|
98
|
+
#
|
99
|
+
# puts browser.text_fields
|
100
|
+
#
|
101
|
+
# @return [String] A string representation of all elements in this collection.
|
102
|
+
#
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
map { |e| e.to_s }.join("\n")
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def iterator_object(i)
|
111
|
+
element_class.new(@container, :index, i + Celerity.index_offset)
|
112
|
+
end
|
113
|
+
|
114
|
+
end # ElementCollection
|
115
|
+
end # Celerity
|