celerity_thingista 0.9.2

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 (118) hide show
  1. data/.document +5 -0
  2. data/.gitignore +9 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +278 -0
  7. data/README.rdoc +84 -0
  8. data/Rakefile +15 -0
  9. data/benchmark/bm_2000_spans.rb +48 -0
  10. data/benchmark/bm_digg.rb +26 -0
  11. data/benchmark/bm_google_images.rb +36 -0
  12. data/benchmark/bm_input_locator.rb +69 -0
  13. data/benchmark/bm_text_input.rb +19 -0
  14. data/benchmark/loader.rb +14 -0
  15. data/celerity.gemspec +26 -0
  16. data/lib/celerity.rb +75 -0
  17. data/lib/celerity/browser.rb +924 -0
  18. data/lib/celerity/clickable_element.rb +73 -0
  19. data/lib/celerity/collections.rb +164 -0
  20. data/lib/celerity/container.rb +802 -0
  21. data/lib/celerity/default_viewer.rb +14 -0
  22. data/lib/celerity/disabled_element.rb +40 -0
  23. data/lib/celerity/element.rb +314 -0
  24. data/lib/celerity/element_collection.rb +115 -0
  25. data/lib/celerity/element_locator.rb +164 -0
  26. data/lib/celerity/elements/button.rb +54 -0
  27. data/lib/celerity/elements/file_field.rb +29 -0
  28. data/lib/celerity/elements/form.rb +22 -0
  29. data/lib/celerity/elements/frame.rb +86 -0
  30. data/lib/celerity/elements/image.rb +89 -0
  31. data/lib/celerity/elements/label.rb +16 -0
  32. data/lib/celerity/elements/link.rb +43 -0
  33. data/lib/celerity/elements/meta.rb +14 -0
  34. data/lib/celerity/elements/non_control_elements.rb +124 -0
  35. data/lib/celerity/elements/option.rb +38 -0
  36. data/lib/celerity/elements/radio_check.rb +114 -0
  37. data/lib/celerity/elements/select_list.rb +146 -0
  38. data/lib/celerity/elements/table.rb +154 -0
  39. data/lib/celerity/elements/table_cell.rb +36 -0
  40. data/lib/celerity/elements/table_elements.rb +42 -0
  41. data/lib/celerity/elements/table_row.rb +54 -0
  42. data/lib/celerity/elements/text_field.rb +168 -0
  43. data/lib/celerity/exception.rb +83 -0
  44. data/lib/celerity/htmlunit.rb +64 -0
  45. data/lib/celerity/htmlunit/commons-codec-1.7.jar +0 -0
  46. data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
  47. data/lib/celerity/htmlunit/commons-io-2.4.jar +0 -0
  48. data/lib/celerity/htmlunit/commons-lang3-3.1.jar +0 -0
  49. data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
  50. data/lib/celerity/htmlunit/cssparser-0.9.9.jar +0 -0
  51. data/lib/celerity/htmlunit/htmlunit-2.12.jar +0 -0
  52. data/lib/celerity/htmlunit/htmlunit-core-js-2.12.jar +0 -0
  53. data/lib/celerity/htmlunit/httpclient-4.2.3.jar +0 -0
  54. data/lib/celerity/htmlunit/httpcore-4.2.2.jar +0 -0
  55. data/lib/celerity/htmlunit/httpmime-4.2.3.jar +0 -0
  56. data/lib/celerity/htmlunit/jetty-http-8.1.9.v20130131.jar +0 -0
  57. data/lib/celerity/htmlunit/jetty-io-8.1.9.v20130131.jar +0 -0
  58. data/lib/celerity/htmlunit/jetty-util-8.1.9.v20130131.jar +0 -0
  59. data/lib/celerity/htmlunit/jetty-websocket-8.1.9.v20130131.jar +0 -0
  60. data/lib/celerity/htmlunit/nekohtml-1.9.18.jar +0 -0
  61. data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
  62. data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
  63. data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
  64. data/lib/celerity/htmlunit/xercesImpl-2.10.0.jar +0 -0
  65. data/lib/celerity/htmlunit/xml-apis-1.4.01.jar +0 -0
  66. data/lib/celerity/identifier.rb +28 -0
  67. data/lib/celerity/ignoring_web_connection.rb +15 -0
  68. data/lib/celerity/input_element.rb +25 -0
  69. data/lib/celerity/javascript_debugger.rb +32 -0
  70. data/lib/celerity/listener.rb +143 -0
  71. data/lib/celerity/resources/no_viewer.png +0 -0
  72. data/lib/celerity/short_inspect.rb +29 -0
  73. data/lib/celerity/util.rb +129 -0
  74. data/lib/celerity/version.rb +3 -0
  75. data/lib/celerity/viewer_connection.rb +89 -0
  76. data/lib/celerity/watir_compatibility.rb +70 -0
  77. data/lib/celerity/xpath_support.rb +50 -0
  78. data/spec/browser_authentication_spec.rb +16 -0
  79. data/spec/browser_spec.rb +439 -0
  80. data/spec/button_spec.rb +24 -0
  81. data/spec/clickable_element_spec.rb +39 -0
  82. data/spec/default_viewer_spec.rb +23 -0
  83. data/spec/element_spec.rb +77 -0
  84. data/spec/filefield_spec.rb +18 -0
  85. data/spec/htmlunit_spec.rb +63 -0
  86. data/spec/implementation.rb +7 -0
  87. data/spec/index_offset_spec.rb +24 -0
  88. data/spec/link_spec.rb +16 -0
  89. data/spec/listener_spec.rb +142 -0
  90. data/spec/spec_helper.rb +6 -0
  91. data/spec/table_spec.rb +41 -0
  92. data/spec/watir_compatibility_spec.rb +32 -0
  93. data/tasks/benchmark.rake +4 -0
  94. data/tasks/check.rake +24 -0
  95. data/tasks/clean.rake +3 -0
  96. data/tasks/fix.rake +25 -0
  97. data/tasks/jar.rake +55 -0
  98. data/tasks/rdoc.rake +4 -0
  99. data/tasks/snapshot.rake +25 -0
  100. data/tasks/spec.rake +27 -0
  101. data/tasks/website.rake +10 -0
  102. data/tasks/yard.rake +16 -0
  103. data/website/benchmarks.html +237 -0
  104. data/website/css/color.css +153 -0
  105. data/website/css/hacks.css +3 -0
  106. data/website/css/layout.css +179 -0
  107. data/website/css/screen.css +5 -0
  108. data/website/css/textmate.css +226 -0
  109. data/website/css/typography.css +72 -0
  110. data/website/gfx/body_bg.gif +0 -0
  111. data/website/gfx/button_bg.jpg +0 -0
  112. data/website/gfx/header_bg.jpg +0 -0
  113. data/website/gfx/header_left.jpg +0 -0
  114. data/website/gfx/header_right.jpg +0 -0
  115. data/website/gfx/nav_bg.jpg +0 -0
  116. data/website/index.html +125 -0
  117. data/website/yard/index.html +1 -0
  118. 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