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,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 elements and input elements with type submit|reset|image|button.
|
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
|
@@ -0,0 +1,22 @@
|
|
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 | [
|
9
|
+
:'accept-charset',
|
10
|
+
:accept,
|
11
|
+
:action,
|
12
|
+
:enctype,
|
13
|
+
:method,
|
14
|
+
:name,
|
15
|
+
:onreset,
|
16
|
+
:onsubmit,
|
17
|
+
:target,
|
18
|
+
]
|
19
|
+
DEFAULT_HOW = :name
|
20
|
+
|
21
|
+
end # Form
|
22
|
+
end # Celerity
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Celerity
|
2
|
+
class Frame < Element
|
3
|
+
include Container
|
4
|
+
include XpathSupport
|
5
|
+
|
6
|
+
attr_accessor :page
|
7
|
+
|
8
|
+
TAGS = [Identifier.new('frame'), Identifier.new('iframe')]
|
9
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [
|
10
|
+
:frameborder,
|
11
|
+
:longdesc,
|
12
|
+
:marginheight,
|
13
|
+
:marginwidth,
|
14
|
+
:name,
|
15
|
+
:noresize,
|
16
|
+
:scrolling,
|
17
|
+
:src,
|
18
|
+
]
|
19
|
+
DEFAULT_HOW = :name
|
20
|
+
|
21
|
+
#
|
22
|
+
# Override the default locate to handle frame and inline frames.
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
|
26
|
+
def locate
|
27
|
+
super
|
28
|
+
if @object
|
29
|
+
@inline_frame_object = @object.getEnclosedWindow.getFrameElement
|
30
|
+
self.page = @object.getEnclosedPage
|
31
|
+
if (frame = self.page.getDocumentElement)
|
32
|
+
@object = frame
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Override assert_exists to raise UnknownFrameException (for Watir compatibility)
|
39
|
+
# @api private
|
40
|
+
#
|
41
|
+
|
42
|
+
def assert_exists
|
43
|
+
locate
|
44
|
+
unless @object
|
45
|
+
raise UnknownFrameException, "unable to locate frame, using #{identifier_string}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Executes the given JavaScript string in this frame. (Celerity only)
|
51
|
+
#
|
52
|
+
|
53
|
+
def execute_script(source)
|
54
|
+
assert_exists
|
55
|
+
@page.executeJavaScript(source.to_s).getJavaScriptResult
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Updates the brwoser page with the page from this frame's top window.
|
60
|
+
# Used internally.
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
#
|
64
|
+
|
65
|
+
def update_page(value)
|
66
|
+
@browser.page = value.getEnclosingWindow.getTopWindow.getEnclosedPage
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
assert_exists
|
71
|
+
Celerity::Util.create_string @inline_frame_object
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_missing(meth, *args, &blk)
|
75
|
+
meth = selector_to_attribute(meth)
|
76
|
+
if self.class::ATTRIBUTES.include?(meth)
|
77
|
+
assert_exists
|
78
|
+
@inline_frame_object.getAttribute(meth.to_s)
|
79
|
+
else
|
80
|
+
Log.warn "Element\#method_missing calling super with #{meth.inspect}"
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end # Frame
|
86
|
+
end # Celerity
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Celerity
|
2
|
+
|
3
|
+
class Image < Element
|
4
|
+
include ClickableElement
|
5
|
+
|
6
|
+
TAGS = [ Identifier.new('img') ]
|
7
|
+
|
8
|
+
ATTRIBUTES = BASE_ATTRIBUTES | [
|
9
|
+
:align,
|
10
|
+
:alt,
|
11
|
+
:border,
|
12
|
+
:height,
|
13
|
+
:hspace,
|
14
|
+
:ismap,
|
15
|
+
:longdesc,
|
16
|
+
:name,
|
17
|
+
:src,
|
18
|
+
:usemap,
|
19
|
+
:vspace,
|
20
|
+
:width,
|
21
|
+
]
|
22
|
+
DEFAULT_HOW = :src
|
23
|
+
|
24
|
+
#
|
25
|
+
# returns the file created date of the image
|
26
|
+
#
|
27
|
+
|
28
|
+
def file_created_date
|
29
|
+
assert_exists
|
30
|
+
web_response = @object.getWebResponse(true)
|
31
|
+
Time.parse(web_response.getResponseHeaderValue("Last-Modified").to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# returns the file size of the image in bytes
|
36
|
+
#
|
37
|
+
|
38
|
+
def file_size
|
39
|
+
assert_exists
|
40
|
+
web_response = @object.getWebResponse(true)
|
41
|
+
web_response.getContentAsString.bytesize
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# returns the width in pixels of the image, as a string
|
46
|
+
#
|
47
|
+
|
48
|
+
def width
|
49
|
+
assert_exists
|
50
|
+
@object.getWidth
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# returns the height in pixels of the image, as a string
|
55
|
+
#
|
56
|
+
|
57
|
+
def height
|
58
|
+
assert_exists
|
59
|
+
@object.getHeight
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# returns true if the image is loaded
|
64
|
+
#
|
65
|
+
|
66
|
+
def loaded?
|
67
|
+
assert_exists
|
68
|
+
begin
|
69
|
+
@object.getImageReader
|
70
|
+
true
|
71
|
+
rescue
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Saves the image to the given file
|
78
|
+
#
|
79
|
+
|
80
|
+
def save(filename)
|
81
|
+
assert_exists
|
82
|
+
image_reader = @object.getImageReader
|
83
|
+
file = java.io.File.new(filename)
|
84
|
+
buffered_image = image_reader.read(0);
|
85
|
+
javax.imageio.ImageIO.write(buffered_image, image_reader.getFormatName(), file);
|
86
|
+
end
|
87
|
+
|
88
|
+
end # Image
|
89
|
+
end # Celerity
|