celerity 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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