celerity 0.0.4 → 0.0.6

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 (158) hide show
  1. data/History.txt +33 -0
  2. data/README.txt +19 -9
  3. data/Rakefile +9 -3
  4. data/lib/celerity.rb +39 -39
  5. data/lib/celerity/browser.rb +538 -153
  6. data/lib/celerity/clickable_element.rb +48 -7
  7. data/lib/celerity/collections.rb +155 -131
  8. data/lib/celerity/container.rb +766 -385
  9. data/lib/celerity/default_viewer.rb +10 -0
  10. data/lib/celerity/disabled_element.rb +19 -2
  11. data/lib/celerity/element.rb +152 -83
  12. data/lib/celerity/element_collection.rb +106 -0
  13. data/lib/celerity/element_locator.rb +89 -66
  14. data/lib/celerity/elements/button.rb +23 -13
  15. data/lib/celerity/elements/file_field.rb +17 -5
  16. data/lib/celerity/elements/form.rb +21 -16
  17. data/lib/celerity/elements/frame.rb +75 -53
  18. data/lib/celerity/elements/image.rb +76 -63
  19. data/lib/celerity/elements/label.rb +4 -2
  20. data/lib/celerity/elements/link.rb +30 -18
  21. data/lib/celerity/elements/meta.rb +6 -0
  22. data/lib/celerity/{non_control_elements.rb → elements/non_control_elements.rb} +106 -76
  23. data/lib/celerity/elements/option.rb +16 -2
  24. data/lib/celerity/elements/radio_check.rb +55 -20
  25. data/lib/celerity/elements/select_list.rb +65 -29
  26. data/lib/celerity/elements/table.rb +141 -94
  27. data/lib/celerity/elements/table_cell.rb +13 -6
  28. data/lib/celerity/elements/{table_body.rb → table_elements.rb} +20 -8
  29. data/lib/celerity/elements/table_row.rb +23 -7
  30. data/lib/celerity/elements/text_field.rb +89 -33
  31. data/lib/celerity/exception.rb +77 -41
  32. data/lib/celerity/extra/method_generator.rb +42 -24
  33. data/lib/celerity/htmlunit.rb +49 -0
  34. data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
  35. data/lib/celerity/htmlunit/htmlunit-2.5-SNAPSHOT.jar +0 -0
  36. data/lib/celerity/htmlunit/htmlunit-core-js-2.5-SNAPSHOT.jar +0 -0
  37. data/lib/celerity/htmlunit/nekohtml-1.9.12-20090308.130127-11.jar +0 -0
  38. data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
  39. data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
  40. data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
  41. data/lib/celerity/identifier.rb +3 -2
  42. data/lib/celerity/input_element.rb +5 -5
  43. data/lib/celerity/listener.rb +135 -0
  44. data/lib/celerity/resources/no_viewer.png +0 -0
  45. data/lib/celerity/util.rb +88 -0
  46. data/lib/celerity/version.rb +4 -3
  47. data/lib/celerity/watir_compatibility.rb +35 -25
  48. data/tasks/jar.rake +57 -0
  49. metadata +35 -142
  50. data.tar.gz.sig +0 -0
  51. data/Manifest.txt +0 -150
  52. data/lib/celerity/element_collections.rb +0 -54
  53. data/lib/celerity/element_map.rb +0 -51
  54. data/lib/celerity/elements/table_footer.rb +0 -30
  55. data/lib/celerity/elements/table_header.rb +0 -30
  56. data/lib/celerity/htmlunit/commons-collections-3.2.jar +0 -0
  57. data/lib/celerity/htmlunit/download.sh +0 -23
  58. data/lib/celerity/htmlunit/htmlunit-2.2.jar +0 -0
  59. data/lib/celerity/htmlunit/htmlunit-core-js-2.2.jar +0 -0
  60. data/lib/celerity/htmlunit/nekohtml-1.9.8.jar +0 -0
  61. data/lib/celerity/htmlunit/xalan-2.7.0.jar +0 -0
  62. data/lib/celerity/htmlunit/xml-apis-1.0.b2.jar +0 -0
  63. data/spec/area_spec.rb +0 -97
  64. data/spec/areas_spec.rb +0 -40
  65. data/spec/browser_spec.rb +0 -266
  66. data/spec/button_spec.rb +0 -227
  67. data/spec/buttons_spec.rb +0 -39
  68. data/spec/checkbox_spec.rb +0 -302
  69. data/spec/checkboxes_spec.rb +0 -38
  70. data/spec/div_spec.rb +0 -207
  71. data/spec/divs_spec.rb +0 -39
  72. data/spec/element_spec.rb +0 -79
  73. data/spec/filefield_spec.rb +0 -123
  74. data/spec/filefields_spec.rb +0 -40
  75. data/spec/form_spec.rb +0 -59
  76. data/spec/forms_spec.rb +0 -41
  77. data/spec/frame_spec.rb +0 -121
  78. data/spec/frames_spec.rb +0 -71
  79. data/spec/hidden_spec.rb +0 -127
  80. data/spec/hiddens_spec.rb +0 -39
  81. data/spec/hn_spec.rb +0 -104
  82. data/spec/hns_spec.rb +0 -45
  83. data/spec/html/2000_spans.html +0 -2009
  84. data/spec/html/bug_duplicate_attributes.html +0 -14
  85. data/spec/html/bug_javascript_001.html +0 -11
  86. data/spec/html/form_js_bug.html +0 -11
  87. data/spec/html/forms_with_input_elements.html +0 -114
  88. data/spec/html/frame_1.html +0 -17
  89. data/spec/html/frame_2.html +0 -16
  90. data/spec/html/frames.html +0 -11
  91. data/spec/html/iframes.html +0 -12
  92. data/spec/html/images.html +0 -27
  93. data/spec/html/images/1.gif +0 -0
  94. data/spec/html/images/2.gif +0 -0
  95. data/spec/html/images/3.gif +0 -0
  96. data/spec/html/images/button.jpg +0 -0
  97. data/spec/html/images/circle.jpg +0 -0
  98. data/spec/html/images/map.gif +0 -0
  99. data/spec/html/images/map2.gif +0 -0
  100. data/spec/html/images/minus.gif +0 -0
  101. data/spec/html/images/originaltriangle.jpg +0 -0
  102. data/spec/html/images/plus.gif +0 -0
  103. data/spec/html/images/square.jpg +0 -0
  104. data/spec/html/images/triangle.jpg +0 -0
  105. data/spec/html/invalid_js.html +0 -11
  106. data/spec/html/latin1_text.html +0 -17
  107. data/spec/html/non_control_elements.html +0 -115
  108. data/spec/html/simple_ajax.html +0 -22
  109. data/spec/html/tables.html +0 -119
  110. data/spec/html/utf8_text.html +0 -15
  111. data/spec/htmlunit_spec.rb +0 -26
  112. data/spec/image_spec.rb +0 -220
  113. data/spec/images_spec.rb +0 -39
  114. data/spec/label_spec.rb +0 -79
  115. data/spec/labels_spec.rb +0 -40
  116. data/spec/li_spec.rb +0 -139
  117. data/spec/link_spec.rb +0 -189
  118. data/spec/links_spec.rb +0 -43
  119. data/spec/lis_spec.rb +0 -40
  120. data/spec/map_spec.rb +0 -102
  121. data/spec/maps_spec.rb +0 -40
  122. data/spec/meta_spec.rb +0 -8
  123. data/spec/ol_spec.rb +0 -87
  124. data/spec/ols_spec.rb +0 -40
  125. data/spec/option_spec.rb +0 -154
  126. data/spec/p_spec.rb +0 -171
  127. data/spec/pre_spec.rb +0 -135
  128. data/spec/pres_spec.rb +0 -40
  129. data/spec/ps_spec.rb +0 -40
  130. data/spec/radio_spec.rb +0 -299
  131. data/spec/radios_spec.rb +0 -42
  132. data/spec/select_list_spec.rb +0 -299
  133. data/spec/select_lists_spec.rb +0 -47
  134. data/spec/span_spec.rb +0 -184
  135. data/spec/spans_spec.rb +0 -64
  136. data/spec/spec.opts +0 -1
  137. data/spec/spec_helper.rb +0 -55
  138. data/spec/table_bodies_spec.rb +0 -57
  139. data/spec/table_body_spec.rb +0 -111
  140. data/spec/table_cell_spec.rb +0 -74
  141. data/spec/table_cells_spec.rb +0 -59
  142. data/spec/table_footer_spec.rb +0 -101
  143. data/spec/table_footers_spec.rb +0 -55
  144. data/spec/table_header_spec.rb +0 -101
  145. data/spec/table_headers_spec.rb +0 -55
  146. data/spec/table_row_spec.rb +0 -104
  147. data/spec/table_rows_spec.rb +0 -58
  148. data/spec/table_spec.rb +0 -160
  149. data/spec/tables_spec.rb +0 -42
  150. data/spec/text_field_spec.rb +0 -323
  151. data/spec/text_fields_spec.rb +0 -44
  152. data/spec/ul_spec.rb +0 -88
  153. data/spec/uls_spec.rb +0 -40
  154. data/spec/watir_compatibility_spec.rb +0 -260
  155. data/support/spec_server.rb +0 -73
  156. data/tasks/rspec.rake +0 -30
  157. data/tasks/specserver.rake +0 -21
  158. metadata.gz.sig +0 -0
data/History.txt CHANGED
@@ -1,3 +1,36 @@
1
+ == 0.0.6 2009-03-19
2
+ * Support for meta, strong, dl, dt, dd, and em HTML elements.
3
+ * Update to HtmlUnit 2.5-SNAPSHOT.
4
+ * New options for Browser#new: :proxy, :charset, :render, :log_level
5
+ * Methods added:
6
+ - Browser#add_cookie
7
+ - Browser#asynchronized
8
+ - Browser#add_listener
9
+ - Browser#content_type
10
+ - Browser#cookies
11
+ - Browser#debug_web_connection
12
+ - Browser#focused_element
13
+ - Browser#io
14
+ - Browser#remove_cookie
15
+ - Browser#response_headers
16
+ - Browser#wait
17
+ - Browser#xml
18
+ - Browser#{element,elements}_by_xpath
19
+ - ClickableElement#{double,right}_click
20
+ - ElementCollection#{first,last}
21
+ * Methods removed:
22
+ - Browser#show_*
23
+ * Methods renamed:
24
+ - SelectList#get_all_contents => SelectList#options
25
+ - SelectList#get_selected_items => selected_options
26
+ - SelectList#clear_selection => SelectList#clear
27
+ * Add support for finding elements by their corresponding <label>.
28
+ * Recognize buttons of type image, reset, submit.
29
+ * Proxy support (see Browser.new)
30
+ * Lots of refactorings, bug fixes and minor enhancements.
31
+
32
+ Thanks to Hirobumi Hama, Kamal Fariz Mahyuddin and Thomas Marek for contributions in this release.
33
+
1
34
  == 0.0.4 2008-08-18
2
35
  * Minor enhancements
3
36
  * Update HtmlUnit to 2.2
data/README.txt CHANGED
@@ -2,14 +2,11 @@
2
2
 
3
3
  * http://celerity.rubyforge.org/
4
4
 
5
- == TUTORIAL:
6
-
7
- * http://celerity.rubyforge.org/wiki/wiki.pl?GettingStarted
8
-
9
5
  == DESCRIPTION:
10
6
 
11
- Celerity is a JRuby library for easy and fast functional test automation for web applications.
12
- It is a wrapper around the HtmlUnit Java library and is currently aimed at providing the same API and functionality as Watir.
7
+ Celerity is a JRuby wrapper around HtmlUnit a headless Java browser with
8
+ JavaScript support. It provides a simple API for programmatic navigation through
9
+ web applications. Celerity aims at being API compatible with Watir.
13
10
 
14
11
  == FEATURES:
15
12
 
@@ -21,13 +18,18 @@ It is a wrapper around the HtmlUnit Java library and is currently aimed at provi
21
18
 
22
19
  == REQUIREMENTS:
23
20
 
24
- * JRuby 1.1 or higher
21
+ * JRuby 1.2.0 or higher
25
22
  * Java 6
26
23
 
27
24
  == INSTALL:
28
25
 
29
- jruby -S gem install celerity
30
-
26
+ `jruby -S gem install celerity`
27
+
28
+ or from GitHub
29
+
30
+ `jruby -S gem install jarib-celerity`
31
+
32
+
31
33
  == EXAMPLE:
32
34
 
33
35
  require "rubygems"
@@ -39,6 +41,14 @@ It is a wrapper around the HtmlUnit Java library and is currently aimed at provi
39
41
  browser.button(:name, 'btnG').click
40
42
 
41
43
  puts "yay" if browser.text.include? 'celerity.rubyforge.org'
44
+
45
+ == SOURCE
46
+
47
+ The source code is available at http://github.com/jarib/celerity/tree/master
48
+
49
+ == WIKI:
50
+
51
+ * http://github.com/jarib/celerity/wikis
42
52
 
43
53
  == LICENSE:
44
54
 
data/Rakefile CHANGED
@@ -1,6 +1,12 @@
1
- begin
1
+ $:.unshift("#{File.dirname(__FILE__)}/lib")
2
+
3
+ if File.exist?('config') # are we in a git clone
2
4
  require 'config/requirements'
3
5
  require 'config/hoe' # setup Hoe + all gem configuration
4
- rescue LoadError
6
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
7
+ else # in gem dir
8
+ load 'tasks/jar.rake'
9
+ load 'tasks/rdoc.rake'
5
10
  end
6
- Dir['tasks/**/*.rake'].each { |rake| load rake }
11
+
12
+
data/lib/celerity.rb CHANGED
@@ -1,60 +1,60 @@
1
1
  $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
2
 
3
- module Celerity
4
- Jars = Dir[File.dirname(__FILE__) + '/celerity/htmlunit/*.jar']
5
- end
3
+ raise "Celerity only works on JRuby at the moment." unless RUBY_PLATFORM =~ /java/
6
4
 
7
- if RUBY_PLATFORM =~ /java/
8
- require 'java'
9
- JavaString = java.lang.String
10
-
11
- Celerity::Jars.each { |jar| require(jar) }
5
+ require 'java'
6
+ require "logger"
7
+ require "uri"
8
+ require "pp"
9
+ require "timeout"
10
+ require "time"
11
+ require 'drb'
12
+ require "fileutils"
13
+ require "thread"
12
14
 
13
- module HtmlUnit
14
- include_package 'com.gargoylesoftware.htmlunit'
15
+ module Celerity
16
+ Log = Logger.new($DEBUG ? $stderr : nil)
17
+ Log.level = Logger::DEBUG
15
18
 
16
- module Html
17
- include_package 'com.gargoylesoftware.htmlunit.html'
18
- end
19
- end
20
-
21
- else
22
- raise "Celerity only works on JRuby at the moment."
19
+ INDEX_OFFSET = 1
20
+ DIR = File.expand_path(File.dirname(__FILE__) + "/celerity")
23
21
  end
24
- # require "rjb"
25
- # Rjb::load(Celerity::Jars.join(";"))
26
- # module HtmlUnit
27
- # WebClient = Rjb::import('com.gargoylesoftware.htmlunit.WebClient')
28
- # BrowserVersion = Rjb::import('com.gargoylesoftware.htmlunit.BrowserVersion')
29
- # end
30
22
 
23
+ require "celerity/htmlunit"
31
24
  require "celerity/version"
32
25
  require "celerity/exception"
33
26
  require "celerity/clickable_element"
34
27
  require "celerity/disabled_element"
35
- require "celerity/element_collections"
28
+ require "celerity/element_collection"
36
29
  require "celerity/collections"
37
30
  require "celerity/element_locator"
38
31
  require "celerity/identifier"
39
32
  require "celerity/container"
40
33
  require "celerity/element"
41
34
  require "celerity/input_element"
42
- require "celerity/non_control_elements"
43
- Dir[File.dirname(__FILE__) + "/celerity/elements/*.rb"].each { |f| require(f) }
44
- require "celerity/element_map"
35
+ require "celerity/elements/non_control_elements"
36
+ require "celerity/elements/button.rb"
37
+ require "celerity/elements/file_field.rb"
38
+ require "celerity/elements/form.rb"
39
+ require "celerity/elements/frame.rb"
40
+ require "celerity/elements/image.rb"
41
+ require "celerity/elements/label.rb"
42
+ require "celerity/elements/link.rb"
43
+ require "celerity/elements/meta.rb"
44
+ require "celerity/elements/option.rb"
45
+ require "celerity/elements/radio_check.rb"
46
+ require "celerity/elements/select_list.rb"
47
+ require "celerity/elements/table.rb"
48
+ require "celerity/elements/table_elements.rb"
49
+ require "celerity/elements/table_cell.rb"
50
+ require "celerity/elements/table_row.rb"
51
+ require "celerity/elements/text_field.rb"
52
+ require "celerity/util"
53
+ require "celerity/default_viewer"
54
+ require "celerity/listener"
45
55
  require "celerity/browser"
46
-
47
56
  require "celerity/watir_compatibility"
48
57
 
49
- require "logger"
50
- require "uri"
51
- require "pp"
52
- require "time"
53
-
54
- Log = Logger.new($DEBUG ? $stderr : nil)
55
- Log.level = Logger::DEBUG
56
58
  # undefine deprecated methods to use them for Element attributes
57
- if ["id", "type"].any? { |meth| Object.instance_methods.include?(meth) }
58
- Object.send :undef_method, :id
59
- Object.send :undef_method, :type
60
- end
59
+ Object.send :undef_method, :id if Object.method_defined? "id"
60
+ Object.send :undef_method, :type if Object.method_defined? "type"
@@ -1,155 +1,492 @@
1
1
  module Celerity
2
2
  class Browser
3
3
  include Container
4
- attr_accessor :page, :object
4
+
5
+ attr_accessor :page, :object, :charset
5
6
  attr_reader :webclient, :viewer
6
-
7
- # Initialize a browser and goto the given URL
8
- # @param uri The URL to go to.
9
- # @return Instance of Celerity::Browser.
7
+
8
+ #
9
+ # Initialize a browser and go to the given URL
10
+ #
11
+ # @param [String] uri The URL to go to.
12
+ # @return [Celerity::Browser] instance.
13
+ #
14
+
10
15
  def self.start(uri)
11
16
  browser = new
12
17
  browser.goto(uri)
13
18
  browser
14
19
  end
15
-
20
+
21
+ #
22
+ # Not implemented. Use ClickableElement#click_and_attach instead.
23
+ #
24
+
16
25
  def self.attach(*args)
17
- raise NotImplementedError, "no popup handling yet"
26
+ raise NotImplementedError, "use ClickableElement#click_and_attach instead"
18
27
  end
19
-
28
+
29
+ def inspect
30
+ vars = (instance_variables - %w[@webclient @browser @object])
31
+ vars = vars.map { |var| "#{var}=#{instance_variable_get(var).inspect}" }.join(" ")
32
+ '#<%s:0x%s %s>' % [self.class.name, self.hash.to_s(16), vars]
33
+ end
34
+
35
+ #
20
36
  # Creates a browser object.
21
37
  #
22
- # @param [Hash[Symbol, Object]] Options for initial configuration of the browser.
23
- #
24
- # @option :browser[:firefox, :internet_explorer] Set the BrowserVersion used by HtmlUnit.
25
- # Defaults to Internet Explorer.
26
- # @option :css[true, false, nil] Enable CSS.
27
- # Disabled by default.
28
- # @option :secure_ssl[true, false, nil] Disable secure SSL.
29
- # Enabled by default.
30
- # @option :resynchronize[true, false, nil] Use HtmlUnit::NicelyResynchronizingAjaxController to resynchronize Ajax calls.
31
- # @option :javascript_exceptions[true, false, nil] Throw exceptions on script errors.
32
- # Disabled by default.
33
- # @option :status_code_exceptions[true, false, nil]Throw exceptions on failing status codes (404 etc.).
34
- # Disabled by default.
38
+ # @see Celerity::Container for an introduction to the main API.
39
+ #
40
+ # @option opts :log_level [Symbol] (:warning) @see log_level=
41
+ # @option opts :browser [:firefox, :internet_explorer] (:internet_explorer) Set the BrowserVersion used by HtmlUnit. Defaults to Internet Explorer.
42
+ # @option opts :css [Boolean] (false) Enable CSS. Disabled by default.
43
+ # @option opts :secure_ssl [Boolean] (true) Disable secure SSL. Enabled by default.
44
+ # @option opts :resynchronize [Boolean] (false) Use HtmlUnit::NicelyResynchronizingAjaxController to resynchronize Ajax calls.
45
+ # @option opts :javascript_exceptions [Boolean] (false) Raise exceptions on script errors. Disabled by default.
46
+ # @option opts :status_code_exceptions [Boolean] (false) Raise exceptions on failing status codes (404 etc.). Disabled by default.
47
+ # @option opts :render [:html, :xml] (:html) What DOM representation to send to connected viewers.
48
+ # @option opts :charset [String] ("UTF-8") Specify the charset that webclient will use by default.
49
+ # @option opts :proxy [String] (nil) Proxy server to use, in address:port format.
35
50
  #
36
51
  # @return [Celerity::Browser] An instance of the browser.
52
+ #
37
53
  # @api public
54
+ #
55
+
38
56
  def initialize(opts = {})
39
- raise TypeError, "bad argument: #{opts.inspect}" unless opts.is_a? Hash
40
-
41
- @opts = opts
57
+ unless opts.is_a?(Hash)
58
+ raise TypeError, "wrong argument type #{opts.class}, expected Hash"
59
+ end
60
+
61
+ unless (render_types = [:html, :xml, nil]).include?(opts[:render])
62
+ raise ArgumentError, "expected one of #{render_types.inspect} for key :render"
63
+ end
64
+
65
+ @render_type = opts.delete(:render) || :html
66
+ @charset = opts.delete(:charset) || "UTF-8"
67
+ self.log_level = opts.delete(:log_level) || :warning
68
+
42
69
  @last_url, @page = nil
43
- @page_container = self
44
70
  @error_checkers = []
45
-
46
- self.log_level = :warning
47
-
48
- browser = @opts[:browser] == :firefox ?
49
- ::HtmlUnit::BrowserVersion::FIREFOX_2 : ::HtmlUnit::BrowserVersion::INTERNET_EXPLORER_7_0
50
-
51
- @webclient = ::HtmlUnit::WebClient.new(browser)
71
+ @browser = self # for Container#browser
52
72
 
53
- configure_webclient
54
- find_viewer
73
+ setup_webclient(opts)
74
+
75
+ raise ArgumentError, "unknown option #{opts.inspect}" unless opts.empty?
76
+ find_viewer
55
77
  end
56
-
78
+
79
+ #
57
80
  # Goto the given URL
58
81
  #
59
- # @param [String] the url
60
- # @return [String] the url
82
+ # @param [String] uri The url.
83
+ # @return [String] The url.
84
+ #
85
+
61
86
  def goto(uri)
62
87
  uri = "http://#{uri}" unless uri =~ %r{://}
63
- self.page = @webclient.getPage(uri)
64
- uri
88
+
89
+ request = HtmlUnit::WebRequestSettings.new(::Java::JavaNet::URL.new(uri))
90
+ request.setCharset(@charset)
91
+
92
+ self.page = @webclient.getPage(request)
93
+
94
+ url()
65
95
  end
66
-
67
- # Unsets the current page (mostly for Watir compatibility)
96
+
97
+ #
98
+ # Set the credentials used for basic HTTP authentication. (Celerity only)
99
+ #
100
+ # Example:
101
+ # browser.credentials = "username:password"
102
+ #
103
+ # @param [String] A string with username / password, separated by a colon
104
+ #
105
+
106
+ def credentials=(string)
107
+ user, pass = string.split(":")
108
+ dcp = HtmlUnit::DefaultCredentialsProvider.new
109
+ dcp.addCredentials(user, pass)
110
+ @webclient.setCredentialsProvider(dcp)
111
+ end
112
+
113
+ #
114
+ # Unsets the current page / closes all windows
115
+ #
116
+
68
117
  def close
69
118
  @page = nil
119
+ @webclient.closeAllWindows
70
120
  end
71
121
 
122
+ #
72
123
  # @return [String] the URL of the current page
124
+ #
125
+
73
126
  def url
74
127
  assert_exists
75
- @page.getWebResponse.getUrl.toString
128
+ @page.getWebResponse.getRequestUrl.toString
76
129
  end
77
-
130
+
131
+ #
78
132
  # @return [String] the title of the current page
133
+ #
134
+
79
135
  def title
80
136
  @page ? @page.getTitleText : ''
81
137
  end
82
138
 
139
+ #
83
140
  # @return [String] the HTML content of the current page
141
+ #
142
+
84
143
  def html
85
144
  @page ? @page.getWebResponse.getContentAsString : ''
86
145
  end
146
+
147
+ #
148
+ # @return [String] the XML representation of the DOM
149
+ #
150
+
151
+ def xml
152
+ return '' unless @page
153
+ return @page.asXml if @page.respond_to?(:asXml)
154
+ return text # fallback to text (for exampel for "plain/text" pages)
155
+ end
87
156
 
157
+ #
88
158
  # @return [String] a text representation of the current page
159
+ #
160
+
89
161
  def text
90
162
  return '' unless @page
91
163
 
92
164
  if @page.respond_to?("getContent")
93
- @page.getContent
165
+ string = @page.getContent.strip
94
166
  else
95
- # # this has minimal whitespace
96
- @page.documentElement.asText
97
-
98
- # if @opts[:browser] == :firefox
99
- # # # this is what firewatir does - only works with HtmlUnit::BrowserVersion::FIREFOX_2
100
- # res = execute_script("document.body.textContent").getJavaScriptResult
101
- # else
102
- # # this only works with HtmlUnit::BrowserVersion::INTERNET_EXPLORER_*, and isn't identical to Watir's ole_object.innerText
103
- # res = execute_script("document.body.innerText").getJavaScriptResult
104
- # end
167
+ string = @page.documentElement.asText.strip
105
168
  end
169
+
170
+ # Celerity::Util.normalize_text(string)
171
+ string
172
+ end
173
+
174
+ #
175
+ # @return [Hash] response headers as a hash
176
+ #
177
+
178
+ def response_headers
179
+ return {} unless @page
180
+
181
+ Hash[*@page.getWebResponse.getResponseHeaders.map { |obj| [obj.name, obj.value] }.flatten]
182
+ end
183
+
184
+ #
185
+ # @return [String] content-type as in 'text/html'
186
+ #
187
+
188
+ def content_type
189
+ return '' unless @page
190
+
191
+ @page.getWebResponse.getContentType
106
192
  end
107
193
 
194
+ #
195
+ # @return [IO, nil] page contents as an IO, returns nil if no page is loaded.
196
+ #
197
+
198
+ def io
199
+ return nil unless @page
200
+
201
+ @page.getWebResponse.getContentAsStream.to_io
202
+ end
203
+
204
+ #
108
205
  # Check if the current page contains the given text.
109
206
  #
110
207
  # @param [String, Regexp] expected_text The text to look for.
111
- # @return [Numeric, nil] The index of the matched text, or nil if it doesn't match.
208
+ # @return [Numeric, nil] The index of the matched text, or nil if it isn't found.
209
+ # @raise [TypeError]
210
+ #
211
+
112
212
  def contains_text(expected_text)
113
213
  return nil unless exist?
114
- case expected_text
115
- when Regexp
116
- text() =~ expected_text
117
- when String
118
- text().index(expected_text)
119
- else
120
- raise ArgumentError, "Argument must be String or Regexp, but was #{expected_text.inspect}:#{expected_text.class}"
121
- end
214
+ super
215
+ end
216
+
217
+ #
218
+ # Get the first element found matching the given XPath.
219
+ #
220
+ # @param [String] xpath
221
+ # @return [Celerity::Element] An element subclass (or Element if none is found)
222
+ #
223
+
224
+ def element_by_xpath(xpath)
225
+ assert_exists
226
+ obj = @page.getFirstByXPath(xpath)
227
+ element_from_dom_node(obj)
228
+ end
229
+
230
+ #
231
+ # Get all the elements matching the given XPath.
232
+ #
233
+ # @param [String] xpath
234
+ # @return [Array<Celerity::Element>] array of elements
235
+ #
236
+
237
+ def elements_by_xpath(xpath)
238
+ assert_exists
239
+ objects = @page.getByXPath(xpath)
240
+ # should use an ElementCollection here?
241
+ objects.map { |o| element_from_dom_node(o) }.compact
122
242
  end
123
243
 
124
- # @return [HtmlUnit::HtmlHtml] the underlying HtmlUnit object.
244
+ #
245
+ # @return [HtmlUnit::HtmlHtml] the underlying HtmlUnit document.
246
+ #
247
+
125
248
  def document
126
249
  @object
127
250
  end
128
-
251
+
252
+ #
129
253
  # Goto the last url - HtmlUnit doesn't have a 'back' functionality, so we only have 1 history item :)
130
254
  # @return [String, nil] The url of the resulting page, or nil if none was stored.
255
+ #
256
+
131
257
  def back
132
- # TODO: this is naive, need capability from HtmlUnit
258
+ # TODO: this is naive, need capability from HtmlUnit
133
259
  goto(@last_url) if @last_url
134
260
  end
135
-
261
+
262
+ #
263
+ # Wait for javascript jobs to finish
264
+ #
265
+
266
+ def wait
267
+ assert_exists
268
+ @page.getEnclosingWindow.getJobManager.waitForAllJobsToFinish(10000)
269
+ end
270
+
271
+ #
136
272
  # Refresh the current page
273
+ #
274
+
137
275
  def refresh
138
276
  assert_exists
139
277
  self.page = @page.refresh
140
278
  end
141
279
 
142
- # Execute the given JavaScript on the current page
143
- # @return [HtmlUnit::ScriptResult]
280
+ #
281
+ # Clears all cookies. (Celerity only)
282
+ #
283
+
284
+ def clear_cookies
285
+ @webclient.getCookieManager.clearCookies
286
+ end
287
+
288
+ #
289
+ # Get the cookies for this session. (Celerity only)
290
+ #
291
+ # @return [Hash<domain, Hash<name, value>>]
292
+ #
293
+
294
+ def cookies
295
+ result = Hash.new { |hash, key| hash[key] = {} }
296
+
297
+ cookies = @webclient.getCookieManager.getCookies
298
+ cookies.each do |cookie|
299
+ result[cookie.getDomain][cookie.getName] = cookie.getValue
300
+ end
301
+
302
+ result
303
+ end
304
+
305
+ #
306
+ # Add a cookie with the given parameters (Celerity only)
307
+ #
308
+ # @param [String] domain
309
+ # @param [String] name
310
+ # @param [String] value
311
+ #
312
+ # @option opts :path [String] ("/") A path
313
+ # @option opts :max_age [Fixnum] (??) A max age
314
+ # @option opts :secure [Boolean] (false)
315
+ #
316
+
317
+ def add_cookie(domain, name, value, opts = {})
318
+ path = opts.delete(:path) || "/"
319
+ max_age = opts.delete(:max_age) || (Time.now + 60*60*24) # not sure if this is correct
320
+ secure = opts.delete(:secure) || false
321
+
322
+ raise "unknown option: #{opts.inspect}" unless opts.empty?
323
+
324
+ cookie = Cookie.new(domain, name, value, path, max_age, secure)
325
+ @webclient.getCookieManager.addCookie(cookie)
326
+ end
327
+
328
+ #
329
+ # Remove the cookie with the given domain and name (Celerity only)
330
+ #
331
+ # @param [String] domain
332
+ # @param [String] name
333
+ #
334
+
335
+ def remove_cookie(domain, name)
336
+ cm = @webclient.getCookieManager
337
+ cookie = cm.getCookies.find { |c| c.getDomain == domain && c.getName == name }
338
+
339
+ if cookie.nil?
340
+ raise "no cookie with domain #{domain.inspect} and name #{name.inspect}"
341
+ end
342
+
343
+ cm.removeCookie(cookie)
344
+ end
345
+
346
+ #
347
+ # Execute the given JavaScript on the current page.
348
+ # @return [Object] The resulting Object
349
+ #
350
+
144
351
  def execute_script(source)
145
352
  assert_exists
146
- @page.executeJavaScript(source.to_s)
353
+ @page.executeJavaScript(source.to_s).getJavaScriptResult
147
354
  end
148
355
 
149
- # Add a 'checker' proc that will be run on every page load
356
+ # experimental - should be removed?
357
+ def send_keys(keys)
358
+ keys = keys.gsub(/\s*/, '').scan(/((?:\{[A-Z]+?\})|.)/u).flatten
359
+ keys.each do |key|
360
+ element = @page.getFocusedElement
361
+ case key
362
+ when "{TAB}"
363
+ @page.tabToNextElement
364
+ when /\w/
365
+ element.type(key)
366
+ else
367
+ raise NotImplementedError
368
+ end
369
+ end
370
+ end
371
+
372
+ #
373
+ # Wait until the given block evaluates to true (Celerity only)
374
+ #
375
+ # @param [Fixnum] timeout Number of seconds to wait before timing out (default: 30).
376
+ # @yieldparam [Celerity::Browser] browser The browser instance.
377
+ # @see Celerity::Browser#resynchronized
378
+ #
379
+
380
+ def wait_until(timeout = 30, &block)
381
+ Timeout.timeout(timeout) do
382
+ until yield(self)
383
+ refresh_page_from_window
384
+ sleep 0.1
385
+ end
386
+ end
387
+ end
388
+
389
+ #
390
+ # Wait while the given block evaluates to true (Celerity only)
391
+ #
392
+ # @param [Fixnum] timeout Number of seconds to wait before timing out (default: 30).
393
+ # @yieldparam [Celerity::Browser] browser The browser instance.
394
+ # @see Celerity::Browser#resynchronized
395
+ #
396
+
397
+ def wait_while(timeout = 30, &block)
398
+ Timeout.timeout(timeout) do
399
+ while yield(self)
400
+ refresh_page_from_window
401
+ sleep 0.1
402
+ end
403
+ end
404
+ end
405
+
406
+ #
407
+ # Allows you to temporarily switch to HtmlUnit's NicelyResynchronizingAjaxController to resynchronize ajax calls.
408
+ #
409
+ # @browser.resynchronized do |b|
410
+ # b.link(:id, 'trigger_ajax_call').click
411
+ # end
412
+ #
413
+ # @yieldparam [Celerity::Browser] browser The current browser object.
414
+ # @see Celerity::Browser#new for how to configure the browser to always use this.
415
+ #
416
+
417
+ def resynchronized(&block)
418
+ old_controller = @webclient.ajaxController
419
+ @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new)
420
+ yield self
421
+ @webclient.setAjaxController(old_controller)
422
+ end
423
+
424
+ #
425
+ # Allows you to temporarliy switch to HtmlUnit's default AjaxController, so ajax calls are performed asynchronously.
426
+ # This is useful if you have created the Browser with :resynchronize => true, but want to switch it off temporarily.
427
+ #
428
+ # @yieldparam [Celerity::Browser] browser The current browser object.
429
+ # @see Celerity::Browser#new
430
+ #
431
+
432
+ def asynchronized(&block)
433
+ old_controller = @webclient.ajaxController
434
+ @webclient.setAjaxController(::HtmlUnit::AjaxController.new)
435
+ yield self
436
+ @webclient.setAjaxController(old_controller)
437
+ end
438
+
439
+ #
440
+ # Start or stop HtmlUnit's DebuggingWebConnection. (Celerity only)
441
+ # The output will go to /tmp/«name»
442
+ #
443
+ # @param [Boolean] bool start or stop
444
+ # @param [String] name required if bool is true
445
+ #
446
+
447
+ def debug_web_connection(bool, name = nil)
448
+ if bool
449
+ raise "no name given" unless name
450
+ @old_webconnection = @webclient.getWebConnection
451
+ dwc = HtmlUnit::Util::DebuggingWebConnection.new(@old_webconnection, name)
452
+ @webclient.setWebConnection(dwc)
453
+ $stderr.puts "debug-webconnection on"
454
+ else
455
+ @webclient.setWebConnection(@old_webconnection) if @old_webconnection
456
+ $stderr.puts "debug-webconnection off"
457
+ end
458
+ end
459
+
460
+ #
461
+ # Add a listener block for one of the available types. (Celerity only)
462
+ # Types map to HtmlUnit interfaces like this:
463
+ #
464
+ # :status => StatusHandler
465
+ # :alert => AlertHandler ( window.alert() )
466
+ # :web_window_event => WebWindowListener
467
+ # :html_parser => HTMLParserListener
468
+ # :incorrectness => IncorrectnessListener
469
+ # :confirm => ConfirmHandler ( window.confirm() )
470
+ # :prompt => PromptHandler ( window.prompt() )
471
+ #
472
+ #
473
+ # @param [Symbol] type One of the above symbols.
474
+ # @param [Proc] block A block to be executed for events of this type.
150
475
  #
151
- # @param [Proc] checker the proc to be run (can also be given as a block)
476
+
477
+ def add_listener(type, &block)
478
+ @listener ||= Celerity::Listener.new(@webclient)
479
+ @listener.add_listener(type, &block)
480
+ end
481
+
482
+ #
483
+ # Add a 'checker' proc that will be run on every page load
484
+ #
485
+ # @param [Proc] checker The proc to be run (can also be given as a block)
486
+ # @yieldparam [Celerity::Browser] browser The current browser object.
152
487
  # @raise [ArgumentError] if no Proc or block was given.
488
+ #
489
+
153
490
  def add_checker(checker = nil, &block)
154
491
  if block_given?
155
492
  @error_checkers << block
@@ -159,138 +496,186 @@ module Celerity
159
496
  raise ArgumentError, "argument must be a Proc or block"
160
497
  end
161
498
  end
162
-
499
+
500
+ #
163
501
  # Remove the given checker from the list of checkers
164
- # @param [Proc] the Proc to disable
502
+ # @param [Proc] checker The Proc to disable.
503
+ #
504
+
165
505
  def disable_checker(checker)
166
506
  @error_checkers.delete(checker)
167
507
  end
168
508
 
509
+ #
510
+ # :finest, :finer, :fine, :config, :info, :warning, :severe, or :off, :all
511
+ #
512
+ # @return [Symbol] the current log level
513
+ #
514
+
515
+ def log_level
516
+ java.util.logging.Logger.getLogger('com.gargoylesoftware.htmlunit').level.to_s.downcase.to_sym
517
+ end
518
+
519
+ #
169
520
  # Set Java log level (default is :warning)
170
521
  #
171
522
  # @param [Symbol] level :finest, :finer, :fine, :config, :info, :warning, :severe, or :off, :all
523
+ #
524
+
172
525
  def log_level=(level)
173
526
  java.util.logging.Logger.getLogger('com.gargoylesoftware.htmlunit').level = java.util.logging.Level.const_get(level.to_s.upcase)
174
527
  end
175
-
176
- # @return [Symbol] the current log level
177
- def log_level
178
- java.util.logging.Logger.getLogger('com.gargoylesoftware.htmlunit').level.to_s.downcase.to_sym
179
- end
180
528
 
181
- # Checks if we have a page currently loaded.
529
+ #
530
+ # Checks if we have a page currently loaded.
182
531
  # @return [true, false]
532
+ #
533
+
183
534
  def exist?
184
535
  !!@page
185
536
  end
186
537
  alias_method :exists?, :exist?
187
538
 
188
- # Allows you to temporarily switch to HtmlUnit's NicelyResynchronizingAjaxController to resynchronize ajax calls.
189
539
  #
190
- # Example:
191
- # @browser.resynchroniced do |b|
192
- # b.link(:id, 'load_fancy_ajax_stuff').click
193
- # end
540
+ # Sets the current page object for the browser
194
541
  #
195
- # @param [block] &block the block to execute synchronized.
196
- def resynchronized(&block)
197
- old_controller = @webclient.ajaxController
198
- @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new)
199
- yield self
200
- @webclient.setAjaxController(old_controller)
542
+ # @param [HtmlUnit::HtmlPage] value The page to set.
543
+ # @api private
544
+ #
545
+
546
+ def page=(value)
547
+ @last_url = url() if exist?
548
+ @page = value
549
+
550
+ if @page.respond_to?("getDocumentElement")
551
+ @object = @page.getDocumentElement
552
+ elsif @page.is_a? HtmlUnit::UnexpectedPage
553
+ raise UnexpectedPageException, @page.getWebResponse.getContentType
554
+ end
555
+
556
+ render unless @viewer == DefaultViewer
557
+ run_error_checks
558
+
559
+ value
201
560
  end
202
561
 
203
- #--
204
- # TODO: could be private?
205
- #++
206
562
  #
207
563
  # Check that we have a @page object.
564
+ #
565
+ # @raise [Celerity::Exception::UnknownObjectException] if no page is loaded.
566
+ # @api private
208
567
  #
209
- # @raise UnknownObjectException
210
- # @api internal
568
+
211
569
  def assert_exists
212
570
  raise UnknownObjectException, "no page loaded" unless exist?
213
571
  end
214
572
 
215
- #--
216
- # TODO: could be private?
217
- #++
573
+ #
574
+ # Returns the element that currently has the focus (Celerity only)
575
+ #
576
+
577
+ def focused_element
578
+ element_from_dom_node(page.getFocusedElement())
579
+ end
580
+
581
+ private
582
+
583
+ #
218
584
  # Runs the all the checker procs added by +add_checker+
219
585
  #
220
586
  # @see add_checker
221
- # @api internal
587
+ # @api private
588
+ #
589
+
222
590
  def run_error_checks
223
- @error_checkers.each { |e| e.call(self) }
591
+ @error_checkers.each { |e| e[self] }
224
592
  end
225
593
 
226
- # Set the current page object for the browser
227
594
  #
228
- # @param [HtmlUnit::HtmlPage] value The page to set.
229
- # @api internal
230
- def page=(value)
231
- @last_url = url() if exist?
232
- @page = value
233
-
234
- if @page.respond_to?("getDocumentElement")
235
- @object = @page.getDocumentElement
595
+ # Configure the webclient according to the options given to #new.
596
+ # @see initialize
597
+ #
598
+
599
+ def setup_webclient(opts)
600
+ browser = (opts.delete(:browser) || :internet_explorer).to_sym
601
+
602
+ case browser
603
+ when :firefox
604
+ browser_version = ::HtmlUnit::BrowserVersion::FIREFOX_2
605
+ when :internet_explorer, :ie
606
+ browser_version = ::HtmlUnit::BrowserVersion::INTERNET_EXPLORER_7_0
607
+ else
608
+ raise ArgumentError, "unknown browser: #{browser.inspect}"
236
609
  end
237
-
238
- render if @viewer
239
- run_error_checks
240
-
241
- value
242
- end
243
610
 
244
- # Used for #show_links(), #show_divs() etc. (for watir compatibility)
245
- def method_missing(meth, *args)
246
- return super unless type = meth.to_s[/^show_(.*)$/, 1]
247
- puts collection_string(type) rescue super
611
+ if proxy = opts.delete(:proxy)
612
+ phost, pport = proxy.split(":")
613
+ @webclient = ::HtmlUnit::WebClient.new(browser_version, phost, pport.to_i)
614
+ else
615
+ @webclient = ::HtmlUnit::WebClient.new(browser_version)
616
+ end
617
+
618
+ @webclient.throwExceptionOnScriptError = false unless opts.delete(:javascript_exceptions)
619
+ @webclient.throwExceptionOnFailingStatusCode = false unless opts.delete(:status_code_exceptions)
620
+ @webclient.cssEnabled = false unless opts.delete(:css)
621
+ @webclient.useInsecureSSL = opts.delete(:secure_ssl) == false
622
+ @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new) if opts.delete(:resynchronize)
248
623
  end
249
624
 
250
- private
625
+ #
626
+ # This *should* be unneccessary, but sometimes the page we get from the
627
+ # window is different (ie. a different object) from our current @page
628
+ # (Used by #wait_while and #wait_until)
629
+ #
251
630
 
252
- # Configure the webclient according to the options given to #new.
253
- # @see initialize
254
- def configure_webclient
255
- @webclient.throwExceptionOnScriptError = false unless @opts[:javascript_exceptions]
256
- @webclient.throwExceptionOnFailingStatusCode = false unless @opts[:status_code_exceptions]
257
- @webclient.cssEnabled = false unless @opts[:css]
258
- @webclient.useInsecureSSL = true if @opts[:secure_ssl]
259
- @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new) if @opts[:resynchronize]
260
- end
261
-
262
- # Create a string representation of all the elements returned by collection_method
263
- # @param [Symbol] collection_method
264
- # @return [String]
265
- def collection_string(collection_method)
266
- collection = self.send collection_method
267
- result = "Found #{collection.size} #{collection_method.downcase}\n"
268
-
269
- collection.each_with_index do |element, index|
270
- result << "#{index+1}: #{element.attribute_string}\n"
631
+ def refresh_page_from_window
632
+ new_page = @page.getEnclosingWindow.getEnclosedPage
633
+
634
+ if new_page && (new_page != @page)
635
+ self.page = new_page
636
+ else
637
+ Log.debug "unneccessary refresh"
271
638
  end
272
-
273
- result
274
639
  end
275
640
 
641
+ #
276
642
  # Render the current page on the viewer.
277
- # @api internal
643
+ # @api private
644
+ #
645
+
278
646
  def render
279
- @viewer.render_html(html, url)
647
+ @viewer.render_html(self.send(@render_type), url)
280
648
  rescue DRb::DRbConnError, Errno::ECONNREFUSED => e
281
- @viewer = nil
282
- end
283
-
649
+ @viewer = DefaultViewer
650
+ end
651
+
652
+ #
284
653
  # Check if we have a viewer available on druby://127.0.0.1:6429
285
- # @api internal
654
+ # @api private
655
+ #
656
+
286
657
  def find_viewer
287
- # FIXME: not ideal
288
- require 'drb'
289
658
  viewer = DRbObject.new_with_uri("druby://127.0.0.1:6429")
290
- @viewer = viewer if viewer.respond_to?(:render_html)
659
+ if viewer.respond_to?(:render_html)
660
+ @viewer = viewer
661
+ else
662
+ @viewer = DefaultViewer
663
+ end
291
664
  rescue DRb::DRbConnError, Errno::ECONNREFUSED
292
- @viewer = nil
665
+ @viewer = DefaultViewer
293
666
  end
667
+
668
+ #
669
+ # Convert the given HtmlUnit object to a Celerity object
670
+ #
294
671
 
672
+ def element_from_dom_node(obj)
673
+ if element_class = Celerity::Util.htmlunit2celerity(obj.class)
674
+ element_class.new(self, :object, obj)
675
+ else
676
+ Element.new(self, :object, nil)
677
+ end
678
+ end
679
+
295
680
  end # Browser
296
- end # Celerity
681
+ end # Celerity