walidhalabi-celerity 0.0.6.11
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.
- data/History.txt +75 -0
- data/License.txt +621 -0
- data/README.txt +73 -0
- data/Rakefile +12 -0
- data/lib/celerity.rb +74 -0
- data/lib/celerity/browser.rb +811 -0
- data/lib/celerity/clickable_element.rb +69 -0
- data/lib/celerity/collections.rb +156 -0
- data/lib/celerity/container.rb +788 -0
- data/lib/celerity/default_viewer.rb +10 -0
- data/lib/celerity/disabled_element.rb +40 -0
- data/lib/celerity/element.rb +313 -0
- data/lib/celerity/element_collection.rb +107 -0
- data/lib/celerity/element_locator.rb +170 -0
- data/lib/celerity/elements/button.rb +43 -0
- data/lib/celerity/elements/file_field.rb +25 -0
- data/lib/celerity/elements/form.rb +23 -0
- data/lib/celerity/elements/frame.rb +75 -0
- data/lib/celerity/elements/image.rb +76 -0
- data/lib/celerity/elements/label.rb +11 -0
- data/lib/celerity/elements/link.rb +30 -0
- data/lib/celerity/elements/meta.rb +6 -0
- data/lib/celerity/elements/non_control_elements.rb +106 -0
- data/lib/celerity/elements/option.rb +32 -0
- data/lib/celerity/elements/radio_check.rb +115 -0
- data/lib/celerity/elements/select_list.rb +121 -0
- data/lib/celerity/elements/table.rb +144 -0
- data/lib/celerity/elements/table_cell.rb +29 -0
- data/lib/celerity/elements/table_elements.rb +42 -0
- data/lib/celerity/elements/table_row.rb +48 -0
- data/lib/celerity/elements/text_field.rb +169 -0
- data/lib/celerity/exception.rb +77 -0
- data/lib/celerity/htmlunit.rb +61 -0
- data/lib/celerity/htmlunit/commons-codec-1.3.jar +0 -0
- data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
- data/lib/celerity/htmlunit/commons-httpclient-3.1.jar +0 -0
- data/lib/celerity/htmlunit/commons-io-1.4.jar +0 -0
- data/lib/celerity/htmlunit/commons-lang-2.4.jar +0 -0
- data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
- data/lib/celerity/htmlunit/cssparser-0.9.5.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-2.6-SNAPSHOT.jar +0 -0
- data/lib/celerity/htmlunit/htmlunit-core-js-2.5.jar +0 -0
- data/lib/celerity/htmlunit/nekohtml-1.9.13-20090507.082850-2.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.8.1.jar +0 -0
- data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
- data/lib/celerity/identifier.rb +11 -0
- data/lib/celerity/input_element.rb +25 -0
- data/lib/celerity/listener.rb +141 -0
- data/lib/celerity/resources/no_viewer.png +0 -0
- data/lib/celerity/short_inspect.rb +20 -0
- data/lib/celerity/util.rb +91 -0
- data/lib/celerity/version.rb +10 -0
- data/lib/celerity/watir_compatibility.rb +84 -0
- data/tasks/jar.rake +57 -0
- data/tasks/rdoc.rake +4 -0
- metadata +130 -0
@@ -0,0 +1,170 @@
|
|
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.getByXPath(what).to_a.first
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_by_label(what)
|
101
|
+
obj = elements_by_tag_names(%w[label]).find { |e| matches?(e.asText, what) }
|
102
|
+
|
103
|
+
return nil unless obj && (ref = obj.getReferencedElement)
|
104
|
+
return ref if @tags.include?(ref.getTagName)
|
105
|
+
|
106
|
+
find_by_id obj.getForAttribute
|
107
|
+
end
|
108
|
+
|
109
|
+
def elements_by_idents(idents = @idents)
|
110
|
+
get_by_idents(:select, idents)
|
111
|
+
end
|
112
|
+
|
113
|
+
def element_by_idents(idents = @idents)
|
114
|
+
get_by_idents(:find, idents)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def get_by_idents(meth, idents)
|
120
|
+
with_nullpointer_retry do
|
121
|
+
@object.getAllHtmlChildElements.send(meth) do |e|
|
122
|
+
next unless @tags.include?(e.getTagName)
|
123
|
+
idents.any? { |id| element_matches_ident?(e, id) }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def element_matches_ident?(element, ident)
|
129
|
+
return false unless ident.tag == element.getTagName
|
130
|
+
|
131
|
+
attr_result = ident.attributes.all? do |key, values|
|
132
|
+
values.any? { |val| matches?(element.getAttribute(key.to_s), val) }
|
133
|
+
end
|
134
|
+
|
135
|
+
if ident.text
|
136
|
+
attr_result && matches?(element.asText.strip, ident.text)
|
137
|
+
else
|
138
|
+
attr_result
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def elements_by_tag_names(tags = @tags)
|
143
|
+
with_nullpointer_retry do
|
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
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# HtmlUnit throws NPEs sometimes when we're locating elements
|
153
|
+
# Retry seems to work fine.
|
154
|
+
def with_nullpointer_retry(max_retries = 3)
|
155
|
+
tries = 0
|
156
|
+
yield
|
157
|
+
rescue java.lang.NullPointerException => e
|
158
|
+
raise e if tries >= max_retries
|
159
|
+
|
160
|
+
tries += 1
|
161
|
+
$stderr.puts "warning: celerity caught #{e} - retry ##{tries}"
|
162
|
+
retry
|
163
|
+
end
|
164
|
+
|
165
|
+
def matches?(string, what)
|
166
|
+
Regexp === what ? string.strip =~ what : string == what.to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
end # ElementLocator
|
170
|
+
end # Celerity
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Celerity
|
2
|
+
|
3
|
+
#
|
4
|
+
# Input: Button
|
5
|
+
#
|
6
|
+
# Class representing button elements
|
7
|
+
#
|
8
|
+
|
9
|
+
class Button < InputElement
|
10
|
+
TAGS = [ Identifier.new('button'),
|
11
|
+
Identifier.new('input', :type => %w[submit reset image button]) ]
|
12
|
+
|
13
|
+
# Attribute list is a little weird due to this class covering both <button>
|
14
|
+
# and <input type="submit|reset|image|button" />
|
15
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [:type, :disabled, :tabindex, :accesskey, :onfocus, :onblur] | [:src, :usemap, :ismap]
|
16
|
+
DEFAULT_HOW = :value
|
17
|
+
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
|
22
|
+
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
|
+
|
26
|
+
if (val = @conditions[:value])
|
27
|
+
button_ident = Identifier.new('button')
|
28
|
+
button_ident.text = val
|
29
|
+
input_ident = Identifier.new('input', :type => %w[submit reset image button], :value => [val])
|
30
|
+
|
31
|
+
locator = ElementLocator.new(@container, self.class)
|
32
|
+
locator.idents = [button_ident, input_ident]
|
33
|
+
|
34
|
+
conditions = @conditions.dup
|
35
|
+
conditions.delete(:value)
|
36
|
+
@object = locator.find_by_conditions(conditions)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end # Button
|
43
|
+
end # Celerity
|
@@ -0,0 +1,25 @@
|
|
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
|
+
@container.update_page @object.setValueAttribute(path)
|
19
|
+
unless @object.getContentType
|
20
|
+
@object.setContentType(Celerity::Util.content_type_for(path))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end # FileField
|
25
|
+
end # Celerity
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Celerity
|
2
|
+
class Form < Element
|
3
|
+
include Container
|
4
|
+
|
5
|
+
TAGS = [Identifier.new('form')]
|
6
|
+
|
7
|
+
# HTML 4.01 Transitional DTD
|
8
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [:action, :method, :enctype, :accept, :name, :onsubmit, :onreset, :target, :'accept-charset']
|
9
|
+
DEFAULT_HOW = :name
|
10
|
+
|
11
|
+
#
|
12
|
+
# Submits the form.
|
13
|
+
#
|
14
|
+
# This method should be avoided - invoke the user interface element that triggers the submit instead.
|
15
|
+
#
|
16
|
+
|
17
|
+
def submit
|
18
|
+
assert_exists
|
19
|
+
@container.update_page @object.submit(nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
end # Form
|
23
|
+
end # Celerity
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Celerity
|
2
|
+
class Frame < Element
|
3
|
+
include Container
|
4
|
+
attr_accessor :page
|
5
|
+
|
6
|
+
TAGS = [Identifier.new('frame'), Identifier.new('iframe')]
|
7
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [:longdesc, :name, :src, :frameborder, :marginwidth, :marginheight, :noresize, :scrolling]
|
8
|
+
DEFAULT_HOW = :name
|
9
|
+
|
10
|
+
#
|
11
|
+
# Override the default locate to handle frame and inline frames.
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
|
15
|
+
def locate
|
16
|
+
super
|
17
|
+
if @object
|
18
|
+
@inline_frame_object = @object.getEnclosedWindow.getFrameElement
|
19
|
+
self.page = @object.getEnclosedPage
|
20
|
+
if (frame = self.page.getDocumentElement)
|
21
|
+
@object = frame
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Override assert_exists to raise UnknownFrameException (for Watir compatibility)
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
|
31
|
+
def assert_exists
|
32
|
+
locate
|
33
|
+
unless @object
|
34
|
+
raise UnknownFrameException, "unable to locate frame, using #{identifier_string}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Executes the given JavaScript string in this frame. (Celerity only)
|
40
|
+
#
|
41
|
+
|
42
|
+
def execute_script(source)
|
43
|
+
assert_exists
|
44
|
+
@page.executeJavaScript(source.to_s).getJavaScriptResult
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Updates the brwoser page with the page from this frame's top window.
|
49
|
+
# Used internally.
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
|
54
|
+
def update_page(value)
|
55
|
+
@browser.page = value.getEnclosingWindow.getTopWindow.getEnclosedPage
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
assert_exists
|
60
|
+
create_string(@inline_frame_object)
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing(meth, *args, &blk)
|
64
|
+
meth = selector_to_attribute(meth)
|
65
|
+
if self.class::ATTRIBUTES.include?(meth)
|
66
|
+
assert_exists
|
67
|
+
@inline_frame_object.getAttribute(meth.to_s)
|
68
|
+
else
|
69
|
+
Log.warn "Element\#method_missing calling super with #{meth.inspect}"
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end # Frame
|
75
|
+
end # Celerity
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Celerity
|
2
|
+
|
3
|
+
class Image < Element
|
4
|
+
include ClickableElement
|
5
|
+
|
6
|
+
TAGS = [ Identifier.new('img') ]
|
7
|
+
|
8
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [:src, :alt, :longdesc, :name, :height, :width, :usemap, :ismap, :align, :border, :hspace, :vspace]
|
9
|
+
DEFAULT_HOW = :src
|
10
|
+
|
11
|
+
#
|
12
|
+
# returns the file created date of the image
|
13
|
+
#
|
14
|
+
|
15
|
+
def file_created_date
|
16
|
+
assert_exists
|
17
|
+
web_response = @object.getWebResponse(true)
|
18
|
+
Time.parse(web_response.getResponseHeaderValue("Last-Modified").to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# returns the file size of the image in bytes
|
23
|
+
#
|
24
|
+
|
25
|
+
def file_size
|
26
|
+
assert_exists
|
27
|
+
web_response = @object.getWebResponse(true)
|
28
|
+
web_response.getContentAsBytes.length
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# returns the width in pixels of the image, as a string
|
33
|
+
#
|
34
|
+
|
35
|
+
def width
|
36
|
+
assert_exists
|
37
|
+
@object.getWidth
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# returns the height in pixels of the image, as a string
|
42
|
+
#
|
43
|
+
|
44
|
+
def height
|
45
|
+
assert_exists
|
46
|
+
@object.getHeight
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# returns true if the image is loaded
|
51
|
+
#
|
52
|
+
|
53
|
+
def loaded?
|
54
|
+
assert_exists
|
55
|
+
begin
|
56
|
+
@object.getImageReader
|
57
|
+
true
|
58
|
+
rescue
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Saves the image to the given file
|
65
|
+
#
|
66
|
+
|
67
|
+
def save(filename)
|
68
|
+
assert_exists
|
69
|
+
image_reader = @object.getImageReader
|
70
|
+
file = java.io.File.new(filename)
|
71
|
+
buffered_image = image_reader.read(0);
|
72
|
+
javax.imageio.ImageIO.write(buffered_image, image_reader.getFormatName(), file);
|
73
|
+
end
|
74
|
+
|
75
|
+
end # Image
|
76
|
+
end # Celerity
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Celerity
|
2
|
+
class Link < Element
|
3
|
+
include ClickableElement
|
4
|
+
|
5
|
+
TAGS = [ Identifier.new('a') ]
|
6
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [:charset, :type, :name, :href, :hreflang,
|
7
|
+
:target, :rel, :rev, :accesskey, :shape,
|
8
|
+
:coords, :tabindex, :onfocus, :onblur]
|
9
|
+
DEFAULT_HOW = :href
|
10
|
+
|
11
|
+
#
|
12
|
+
# Returns the absolute URL for this link (Celerity-specific)
|
13
|
+
#
|
14
|
+
# (Watir/IE does this for href(), but we don't want that.)
|
15
|
+
#
|
16
|
+
|
17
|
+
def absolute_url
|
18
|
+
assert_exists
|
19
|
+
href = @object.getAttribute('href')
|
20
|
+
|
21
|
+
unless href.empty? || URI.parse(href).absolute?
|
22
|
+
href = URI.join(browser.url, href).to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
href
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end # Link
|
30
|
+
end # Celerity
|