walidhalabi-celerity 0.0.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. data/History.txt +75 -0
  2. data/License.txt +621 -0
  3. data/README.txt +73 -0
  4. data/Rakefile +12 -0
  5. data/lib/celerity.rb +74 -0
  6. data/lib/celerity/browser.rb +811 -0
  7. data/lib/celerity/clickable_element.rb +69 -0
  8. data/lib/celerity/collections.rb +156 -0
  9. data/lib/celerity/container.rb +788 -0
  10. data/lib/celerity/default_viewer.rb +10 -0
  11. data/lib/celerity/disabled_element.rb +40 -0
  12. data/lib/celerity/element.rb +313 -0
  13. data/lib/celerity/element_collection.rb +107 -0
  14. data/lib/celerity/element_locator.rb +170 -0
  15. data/lib/celerity/elements/button.rb +43 -0
  16. data/lib/celerity/elements/file_field.rb +25 -0
  17. data/lib/celerity/elements/form.rb +23 -0
  18. data/lib/celerity/elements/frame.rb +75 -0
  19. data/lib/celerity/elements/image.rb +76 -0
  20. data/lib/celerity/elements/label.rb +11 -0
  21. data/lib/celerity/elements/link.rb +30 -0
  22. data/lib/celerity/elements/meta.rb +6 -0
  23. data/lib/celerity/elements/non_control_elements.rb +106 -0
  24. data/lib/celerity/elements/option.rb +32 -0
  25. data/lib/celerity/elements/radio_check.rb +115 -0
  26. data/lib/celerity/elements/select_list.rb +121 -0
  27. data/lib/celerity/elements/table.rb +144 -0
  28. data/lib/celerity/elements/table_cell.rb +29 -0
  29. data/lib/celerity/elements/table_elements.rb +42 -0
  30. data/lib/celerity/elements/table_row.rb +48 -0
  31. data/lib/celerity/elements/text_field.rb +169 -0
  32. data/lib/celerity/exception.rb +77 -0
  33. data/lib/celerity/htmlunit.rb +61 -0
  34. data/lib/celerity/htmlunit/commons-codec-1.3.jar +0 -0
  35. data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
  36. data/lib/celerity/htmlunit/commons-httpclient-3.1.jar +0 -0
  37. data/lib/celerity/htmlunit/commons-io-1.4.jar +0 -0
  38. data/lib/celerity/htmlunit/commons-lang-2.4.jar +0 -0
  39. data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
  40. data/lib/celerity/htmlunit/cssparser-0.9.5.jar +0 -0
  41. data/lib/celerity/htmlunit/htmlunit-2.6-SNAPSHOT.jar +0 -0
  42. data/lib/celerity/htmlunit/htmlunit-core-js-2.5.jar +0 -0
  43. data/lib/celerity/htmlunit/nekohtml-1.9.13-20090507.082850-2.jar +0 -0
  44. data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
  45. data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
  46. data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
  47. data/lib/celerity/htmlunit/xercesImpl-2.8.1.jar +0 -0
  48. data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
  49. data/lib/celerity/identifier.rb +11 -0
  50. data/lib/celerity/input_element.rb +25 -0
  51. data/lib/celerity/listener.rb +141 -0
  52. data/lib/celerity/resources/no_viewer.png +0 -0
  53. data/lib/celerity/short_inspect.rb +20 -0
  54. data/lib/celerity/util.rb +91 -0
  55. data/lib/celerity/version.rb +10 -0
  56. data/lib/celerity/watir_compatibility.rb +84 -0
  57. data/tasks/jar.rake +57 -0
  58. data/tasks/rdoc.rake +4 -0
  59. metadata +130 -0
data/README.txt ADDED
@@ -0,0 +1,73 @@
1
+ = Celerity
2
+
3
+ * http://celerity.rubyforge.org/
4
+
5
+ == DESCRIPTION:
6
+
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.
10
+
11
+ == FEATURES:
12
+
13
+ * Fast: No time-consuming GUI rendering or unessential downloads
14
+ * Scalable: Java threads lets you run tests in parallel
15
+ * Easy to use: Simple API
16
+ * Portable: Cross-platform
17
+ * Unintrusive: No browser window interrupting your workflow (runs in background)
18
+
19
+ == REQUIREMENTS:
20
+
21
+ * JRuby 1.2.0 or higher
22
+ * Java 6
23
+
24
+ == INSTALL:
25
+
26
+ `jruby -S gem install celerity`
27
+
28
+ or from GitHub
29
+
30
+ `jruby -S gem install jarib-celerity`
31
+
32
+
33
+ == EXAMPLE:
34
+
35
+ require "rubygems"
36
+ require "celerity"
37
+
38
+ browser = Celerity::Browser.new
39
+ browser.goto('http://www.google.com')
40
+ browser.text_field(:name, 'q').value = 'Celerity'
41
+ browser.button(:name, 'btnG').click
42
+
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
52
+
53
+ == BUG TRACKER:
54
+
55
+ * http://github.com/jarib/celerity/issues
56
+
57
+ == LICENSE:
58
+
59
+ Celerity - JRuby wrapper for HtmlUnit
60
+ Copyright (c) 2008 FINN.no AS
61
+
62
+ This program is free software: you can redistribute it and/or modify
63
+ it under the terms of the GNU General Public License as published by
64
+ the Free Software Foundation, either version 3 of the License, or
65
+ (at your option) any later version.
66
+
67
+ This program is distributed in the hope that it will be useful,
68
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
69
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
70
+ GNU General Public License for more details.
71
+
72
+ You should have received a copy of the GNU General Public License
73
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ $:.unshift("#{File.dirname(__FILE__)}/lib")
2
+
3
+ if File.exist?('config') # are we in a git clone
4
+ require 'config/requirements'
5
+ require 'config/hoe' # setup Hoe + all gem configuration
6
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
7
+ else # in gem dir
8
+ load 'tasks/jar.rake'
9
+ load 'tasks/rdoc.rake'
10
+ end
11
+
12
+
data/lib/celerity.rb ADDED
@@ -0,0 +1,74 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ raise "Celerity only works on JRuby at the moment." unless RUBY_PLATFORM =~ /java/
4
+
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"
14
+
15
+ module Celerity
16
+ Log = Logger.new($DEBUG ? $stderr : nil)
17
+ Log.level = Logger::DEBUG
18
+
19
+ @index_offset = 1
20
+ class << self
21
+
22
+ #
23
+ # This index_offset attribute controls the indexing used when locating
24
+ # elements by :index or fetching from Celerity::ElementCollections.
25
+ #
26
+ # By default it is set to 1 for Watir compatibility, but users who use
27
+ # Celerity exlusively may want it set to 0 to make Celerity more consistent with Ruby.
28
+ #
29
+ def index_offset=(n) @index_offset = n end
30
+ def index_offset() @index_offset end
31
+ end
32
+
33
+ DIR = File.expand_path(File.dirname(__FILE__) + "/celerity")
34
+ end
35
+
36
+ require "celerity/htmlunit"
37
+ require "celerity/version"
38
+ require "celerity/exception"
39
+ require "celerity/clickable_element"
40
+ require "celerity/disabled_element"
41
+ require "celerity/element_collection"
42
+ require "celerity/collections"
43
+ require "celerity/element_locator"
44
+ require "celerity/identifier"
45
+ require "celerity/short_inspect"
46
+ require "celerity/container"
47
+ require "celerity/element"
48
+ require "celerity/input_element"
49
+ require "celerity/elements/non_control_elements"
50
+ require "celerity/elements/button.rb"
51
+ require "celerity/elements/file_field.rb"
52
+ require "celerity/elements/form.rb"
53
+ require "celerity/elements/frame.rb"
54
+ require "celerity/elements/image.rb"
55
+ require "celerity/elements/label.rb"
56
+ require "celerity/elements/link.rb"
57
+ require "celerity/elements/meta.rb"
58
+ require "celerity/elements/option.rb"
59
+ require "celerity/elements/radio_check.rb"
60
+ require "celerity/elements/select_list.rb"
61
+ require "celerity/elements/table.rb"
62
+ require "celerity/elements/table_elements.rb"
63
+ require "celerity/elements/table_cell.rb"
64
+ require "celerity/elements/table_row.rb"
65
+ require "celerity/elements/text_field.rb"
66
+ require "celerity/util"
67
+ require "celerity/default_viewer"
68
+ require "celerity/listener"
69
+ require "celerity/browser"
70
+ require "celerity/watir_compatibility"
71
+
72
+ # undefine deprecated methods to use them for Element attributes
73
+ Object.send :undef_method, :id if Object.method_defined? "id"
74
+ Object.send :undef_method, :type if Object.method_defined? "type"
@@ -0,0 +1,811 @@
1
+ module Celerity
2
+ class Browser
3
+ include Container
4
+
5
+ attr_accessor :page, :object, :charset
6
+ attr_reader :webclient, :viewer, :options
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
+
15
+ def self.start(uri)
16
+ browser = new
17
+ browser.goto(uri)
18
+ browser
19
+ end
20
+
21
+ #
22
+ # Not implemented. Use ClickableElement#click_and_attach instead.
23
+ #
24
+
25
+ def self.attach(*args)
26
+ raise NotImplementedError, "use ClickableElement#click_and_attach instead"
27
+ end
28
+
29
+ #
30
+ # Creates a browser object.
31
+ #
32
+ # @see Celerity::Container for an introduction to the main API.
33
+ #
34
+ # @option opts :log_level [Symbol] (:warning) @see log_level=
35
+ # @option opts :browser [:firefox, :internet_explorer] (:internet_explorer) Set the BrowserVersion used by HtmlUnit. Defaults to Internet Explorer.
36
+ # @option opts :css [Boolean] (false) Enable CSS. Disabled by default.
37
+ # @option opts :secure_ssl [Boolean] (true) Disable secure SSL. Enabled by default.
38
+ # @option opts :resynchronize [Boolean] (false) Use HtmlUnit::NicelyResynchronizingAjaxController to resynchronize Ajax calls.
39
+ # @option opts :javascript_exceptions [Boolean] (false) Raise exceptions on script errors. Disabled by default.
40
+ # @option opts :status_code_exceptions [Boolean] (false) Raise exceptions on failing status codes (404 etc.). Disabled by default.
41
+ # @option opts :render [:html, :xml] (:html) What DOM representation to send to connected viewers.
42
+ # @option opts :charset [String] ("UTF-8") Specify the charset that webclient will use for requests, and those where texts are getting gibberished, like Browser#html.
43
+ # @option opts :proxy [String] (nil) Proxy server to use, in address:port format.
44
+ # @option opts :user_agent [String] Override the User-Agent set by the :browser option
45
+ #
46
+ # @return [Celerity::Browser] An instance of the browser.
47
+ #
48
+ # @api public
49
+ #
50
+
51
+ def initialize(opts = {})
52
+ unless opts.is_a?(Hash)
53
+ raise TypeError, "wrong argument type #{opts.class}, expected Hash"
54
+ end
55
+
56
+ unless (render_types = [:html, :xml, nil]).include?(opts[:render])
57
+ raise ArgumentError, "expected one of #{render_types.inspect} for key :render"
58
+ end
59
+
60
+ @options = opts.dup # for ClickableElement#click_and_attach
61
+
62
+ @render_type = opts.delete(:render) || :html
63
+ @charset = opts.delete(:charset) || "UTF-8"
64
+ self.log_level = opts.delete(:log_level) || :warning
65
+
66
+ @last_url, @page = nil
67
+ @error_checkers = []
68
+ @browser = self # for Container#browser
69
+
70
+ setup_webclient(opts)
71
+
72
+ raise ArgumentError, "unknown option #{opts.inspect}" unless opts.empty?
73
+ find_viewer
74
+ end
75
+
76
+ def inspect
77
+ short_inspect :exclude => %w[@webclient @browser @object @options]
78
+ end
79
+
80
+ #
81
+ # Goto the given URL
82
+ #
83
+ # @param [String] uri The url.
84
+ # @return [String] The url.
85
+ #
86
+
87
+ def goto(uri)
88
+ uri = "http://#{uri}" unless uri =~ %r{://}
89
+
90
+ request = HtmlUnit::WebRequestSettings.new(::Java::JavaNet::URL.new(uri))
91
+ request.setCharset(@charset)
92
+
93
+ rescue_status_code_exception do
94
+ self.page = @webclient.getPage(request)
95
+ end
96
+
97
+ url()
98
+ end
99
+
100
+ #
101
+ # Set the credentials used for basic HTTP authentication. (Celerity only)
102
+ #
103
+ # Example:
104
+ # browser.credentials = "username:password"
105
+ #
106
+ # @param [String] A string with username / password, separated by a colon
107
+ #
108
+
109
+ def credentials=(string)
110
+ user, pass = string.split(":")
111
+ dcp = HtmlUnit::DefaultCredentialsProvider.new
112
+ dcp.addCredentials(user, pass)
113
+ @webclient.setCredentialsProvider(dcp)
114
+ end
115
+
116
+ #
117
+ # Unsets the current page / closes all windows
118
+ #
119
+
120
+ def close
121
+ @page = nil
122
+ @webclient.closeAllWindows
123
+ end
124
+
125
+ #
126
+ # @return [String] the URL of the current page
127
+ #
128
+
129
+ def url
130
+ assert_exists
131
+ @page.getWebResponse.getRequestUrl.toString
132
+ end
133
+
134
+ #
135
+ # @return [String] the title of the current page
136
+ #
137
+
138
+ def title
139
+ @page ? @page.getTitleText : ''
140
+ end
141
+
142
+ #
143
+ # @return [String] the value of window.status
144
+ #
145
+
146
+ def status
147
+ execute_script "window.status" # avoid the listener overhead
148
+ end
149
+
150
+ #
151
+ # @return [String] the HTML content of the current page
152
+ #
153
+
154
+ def html
155
+ @page ? @page.getWebResponse.getContentAsString(@charset) : ''
156
+ end
157
+
158
+ #
159
+ # @return [String] the XML representation of the DOM
160
+ #
161
+
162
+ def xml
163
+ return '' unless @page
164
+ return @page.asXml if @page.respond_to?(:asXml)
165
+ return text # fallback to text (for exampel for "plain/text" pages)
166
+ end
167
+
168
+ #
169
+ # @return [String] a text representation of the current page
170
+ #
171
+
172
+ def text
173
+ return '' unless @page
174
+
175
+ if @page.respond_to?("getContent")
176
+ string = @page.getContent.strip
177
+ elsif @page.documentElement
178
+ string = @page.documentElement.asText.strip
179
+ else
180
+ string = ''
181
+ end
182
+
183
+ # Celerity::Util.normalize_text(string)
184
+ string
185
+ end
186
+
187
+ #
188
+ # @return [Hash] response headers as a hash
189
+ #
190
+
191
+ def response_headers
192
+ return {} unless @page
193
+
194
+ Hash[*@page.getWebResponse.getResponseHeaders.map { |obj| [obj.name, obj.value] }.flatten]
195
+ end
196
+
197
+ #
198
+ # @return [Fixnum] status code of the last request
199
+ #
200
+
201
+ def status_code
202
+ @page.getWebResponse.getStatusCode
203
+ end
204
+
205
+ #
206
+ # @return [String] content-type as in 'text/html'
207
+ #
208
+
209
+ def content_type
210
+ return '' unless @page
211
+
212
+ @page.getWebResponse.getContentType
213
+ end
214
+
215
+ #
216
+ # @return [IO, nil] page contents as an IO, returns nil if no page is loaded.
217
+ #
218
+
219
+ def io
220
+ return nil unless @page
221
+
222
+ @page.getWebResponse.getContentAsStream.to_io
223
+ end
224
+
225
+ #
226
+ # Check if the current page contains the given text.
227
+ #
228
+ # @param [String, Regexp] expected_text The text to look for.
229
+ # @return [Numeric, nil] The index of the matched text, or nil if it isn't found.
230
+ # @raise [TypeError]
231
+ #
232
+
233
+ def contains_text(expected_text)
234
+ return nil unless exist?
235
+ super
236
+ end
237
+
238
+ #
239
+ # Get the first element found matching the given XPath.
240
+ #
241
+ # @param [String] xpath
242
+ # @return [Celerity::Element] An element subclass (or Element if none is found)
243
+ #
244
+
245
+ def element_by_xpath(xpath)
246
+ assert_exists
247
+ obj = @page.getFirstByXPath(xpath)
248
+ element_from_dom_node(obj)
249
+ end
250
+
251
+ #
252
+ # Get all the elements matching the given XPath.
253
+ #
254
+ # @param [String] xpath
255
+ # @return [Array<Celerity::Element>] array of elements
256
+ #
257
+
258
+ def elements_by_xpath(xpath)
259
+ assert_exists
260
+ objects = @page.getByXPath(xpath)
261
+ # should use an ElementCollection here?
262
+ objects.map { |o| element_from_dom_node(o) }.compact
263
+ end
264
+
265
+ #
266
+ # @return [HtmlUnit::HtmlHtml] the underlying HtmlUnit document.
267
+ #
268
+
269
+ def document
270
+ @object
271
+ end
272
+
273
+ #
274
+ # Goto the last url - HtmlUnit doesn't have a 'back' functionality, so we only have 1 history item :)
275
+ # @return [String, nil] The url of the resulting page, or nil if none was stored.
276
+ #
277
+
278
+ def back
279
+ # TODO: this is naive, need capability from HtmlUnit
280
+ goto(@last_url) if @last_url
281
+ end
282
+
283
+ #
284
+ # Wait for javascript jobs to finish
285
+ #
286
+
287
+ def wait
288
+ assert_exists
289
+ @webclient.waitForBackgroundJavaScript(10000);
290
+ end
291
+
292
+ #
293
+ # Refresh the current page
294
+ #
295
+
296
+ def refresh
297
+ assert_exists
298
+ self.page = @page.refresh
299
+ end
300
+
301
+ #
302
+ # Clears all cookies. (Celerity only)
303
+ #
304
+
305
+ def clear_cookies
306
+ @webclient.getCookieManager.clearCookies
307
+ end
308
+
309
+ #
310
+ # Clears the cache of "compiled JavaScript files and parsed CSS snippets"
311
+ #
312
+
313
+ def clear_cache
314
+ @webclient.cache.clear
315
+ end
316
+
317
+ #
318
+ # Get the cookies for this session. (Celerity only)
319
+ #
320
+ # @return [Hash<domain, Hash<name, value>>]
321
+ #
322
+
323
+ def cookies
324
+ result = Hash.new { |hash, key| hash[key] = {} }
325
+
326
+ cookies = @webclient.getCookieManager.getCookies
327
+ cookies.each do |cookie|
328
+ result[cookie.getDomain][cookie.getName] = cookie.getValue
329
+ end
330
+
331
+ result
332
+ end
333
+
334
+ #
335
+ # Add a cookie with the given parameters (Celerity only)
336
+ #
337
+ # @param [String] domain
338
+ # @param [String] name
339
+ # @param [String] value
340
+ #
341
+ # @option opts :path [String] ("/") A path
342
+ # @option opts :max_age [Fixnum] (??) A max age
343
+ # @option opts :secure [Boolean] (false)
344
+ #
345
+
346
+ def add_cookie(domain, name, value, opts = {})
347
+ path = opts.delete(:path) || "/"
348
+ max_age = opts.delete(:max_age) || (Time.now + 60*60*24) # not sure if this is correct
349
+ secure = opts.delete(:secure) || false
350
+
351
+ raise "unknown option: #{opts.inspect}" unless opts.empty?
352
+
353
+ cookie = Cookie.new(domain, name, value, path, max_age, secure)
354
+ @webclient.getCookieManager.addCookie(cookie)
355
+ end
356
+
357
+ #
358
+ # Remove the cookie with the given domain and name (Celerity only)
359
+ #
360
+ # @param [String] domain
361
+ # @param [String] name
362
+ #
363
+
364
+ def remove_cookie(domain, name)
365
+ cm = @webclient.getCookieManager
366
+ cookie = cm.getCookies.find { |c| c.getDomain == domain && c.getName == name }
367
+
368
+ if cookie.nil?
369
+ raise "no cookie with domain #{domain.inspect} and name #{name.inspect}"
370
+ end
371
+
372
+ cm.removeCookie(cookie)
373
+ end
374
+
375
+ #
376
+ # Execute the given JavaScript on the current page.
377
+ # @return [Object] The resulting Object
378
+ #
379
+
380
+ def execute_script(source)
381
+ assert_exists
382
+ @page.executeJavaScript(source.to_s).getJavaScriptResult
383
+ end
384
+
385
+ # experimental - should be removed?
386
+ def send_keys(keys)
387
+ keys = keys.gsub(/\s*/, '').scan(/((?:\{[A-Z]+?\})|.)/u).flatten
388
+ keys.each do |key|
389
+ element = @page.getFocusedElement
390
+ case key
391
+ when "{TAB}"
392
+ @page.tabToNextElement
393
+ when /\w/
394
+ element.type(key)
395
+ else
396
+ raise NotImplementedError
397
+ end
398
+ end
399
+ end
400
+
401
+ #
402
+ # Wait until the given block evaluates to true (Celerity only)
403
+ #
404
+ # @param [Fixnum] timeout Number of seconds to wait before timing out (default: 30).
405
+ # @yieldparam [Celerity::Browser] browser The browser instance.
406
+ # @see Celerity::Browser#resynchronized
407
+ #
408
+
409
+ def wait_until(timeout = 30, &block)
410
+ Timeout.timeout(timeout) do
411
+ until yield(self)
412
+ refresh_page_from_window
413
+ sleep 0.1
414
+ end
415
+ end
416
+ end
417
+
418
+ #
419
+ # Wait while the given block evaluates to true (Celerity only)
420
+ #
421
+ # @param [Fixnum] timeout Number of seconds to wait before timing out (default: 30).
422
+ # @yieldparam [Celerity::Browser] browser The browser instance.
423
+ # @see Celerity::Browser#resynchronized
424
+ #
425
+
426
+ def wait_while(timeout = 30, &block)
427
+ Timeout.timeout(timeout) do
428
+ while yield(self)
429
+ refresh_page_from_window
430
+ sleep 0.1
431
+ end
432
+ end
433
+ end
434
+
435
+ #
436
+ # Allows you to temporarily switch to HtmlUnit's NicelyResynchronizingAjaxController
437
+ # to resynchronize ajax calls.
438
+ #
439
+ # @browser.resynchronized do |b|
440
+ # b.link(:id, 'trigger_ajax_call').click
441
+ # end
442
+ #
443
+ # @yieldparam [Celerity::Browser] browser The current browser object.
444
+ # @see Celerity::Browser#new for how to configure the browser to always use this.
445
+ #
446
+
447
+ def resynchronized(&block)
448
+ old_controller = @webclient.ajaxController
449
+ @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new)
450
+ yield self
451
+ @webclient.setAjaxController(old_controller)
452
+ end
453
+
454
+ #
455
+ # Allows you to temporarliy switch to HtmlUnit's default AjaxController, so
456
+ # ajax calls are performed asynchronously. This is useful if you have created
457
+ # the Browser with :resynchronize => true, but want to switch it off temporarily.
458
+ #
459
+ # @yieldparam [Celerity::Browser] browser The current browser object.
460
+ # @see Celerity::Browser#new
461
+ #
462
+
463
+ def asynchronized(&block)
464
+ old_controller = @webclient.ajaxController
465
+ @webclient.setAjaxController(::HtmlUnit::AjaxController.new)
466
+ yield self
467
+ @webclient.setAjaxController(old_controller)
468
+ end
469
+
470
+ #
471
+ # Start or stop HtmlUnit's DebuggingWebConnection. (Celerity only)
472
+ # The output will go to /tmp/«name»
473
+ #
474
+ # @param [Boolean] bool start or stop
475
+ # @param [String] name required if bool is true
476
+ #
477
+
478
+ def debug_web_connection(bool, name = nil)
479
+ if bool
480
+ raise "no name given" unless name
481
+ @old_webconnection = @webclient.getWebConnection
482
+ dwc = HtmlUnit::Util::DebuggingWebConnection.new(@old_webconnection, name)
483
+ @webclient.setWebConnection(dwc)
484
+ $stderr.puts "debug-webconnection on"
485
+ else
486
+ @webclient.setWebConnection(@old_webconnection) if @old_webconnection
487
+ $stderr.puts "debug-webconnection off"
488
+ end
489
+ end
490
+
491
+ #
492
+ # Add a listener block for one of the available types. (Celerity only)
493
+ # Types map to HtmlUnit interfaces like this:
494
+ #
495
+ # :status => StatusHandler
496
+ # :alert => AlertHandler ( window.alert() )
497
+ # :web_window_event => WebWindowListener
498
+ # :html_parser => HTMLParserListener
499
+ # :incorrectness => IncorrectnessListener
500
+ # :confirm => ConfirmHandler ( window.confirm() )
501
+ # :prompt => PromptHandler ( window.prompt() )
502
+ #
503
+ # Examples:
504
+ #
505
+ # browser.add_listener(:status) { |page, message| ... }
506
+ # browser.add_listener(:alert) { |page, message| ... }
507
+ # browser.add_listener(:web_window_event) { |web_window_event| ... }
508
+ # browser.add_listener(:html_parser) { |message, url, line, column, key| ... }
509
+ # browser.add_listener(:incorrectness) { |message, origin| ... }
510
+ # browser.add_listener(:confirm) { |page, message| ...; true }
511
+ # browser.add_listener(:prompt) { |page, message| ... }
512
+ #
513
+ #
514
+ # @param [Symbol] type One of the above symbols.
515
+ # @param [Proc] block A block to be executed for events of this type.
516
+ #
517
+
518
+ def add_listener(type, &block)
519
+ listener.add_listener(type, &block)
520
+ end
521
+
522
+ #
523
+ # Specify a boolean value to click either 'OK' or 'Cancel' in any confirm
524
+ # dialogs that might show up during the duration of the given block.
525
+ #
526
+ # (Celerity only)
527
+ #
528
+ # @param [Boolean] bool true to click 'OK', false to click 'cancel'
529
+ # @param [Proc] block A block that will trigger the confirm() call(s).
530
+ #
531
+
532
+ def confirm(bool, &block)
533
+ blk = lambda { bool }
534
+
535
+ listener.add_listener(:confirm, &blk)
536
+ yield
537
+ listener.remove_listener(:confirm, blk)
538
+ end
539
+
540
+ #
541
+ # Add a 'checker' proc that will be run on every page load
542
+ #
543
+ # @param [Proc] checker The proc to be run (can also be given as a block)
544
+ # @yieldparam [Celerity::Browser] browser The current browser object.
545
+ # @raise [ArgumentError] if no Proc or block was given.
546
+ #
547
+
548
+ def add_checker(checker = nil, &block)
549
+ if block_given?
550
+ @error_checkers << block
551
+ elsif Proc === checker
552
+ @error_checkers << checker
553
+ else
554
+ raise ArgumentError, "argument must be a Proc or block"
555
+ end
556
+ end
557
+
558
+ #
559
+ # Remove the given checker from the list of checkers
560
+ # @param [Proc] checker The Proc to disable.
561
+ #
562
+
563
+ def disable_checker(checker)
564
+ @error_checkers.delete(checker)
565
+ end
566
+
567
+ #
568
+ # :finest, :finer, :fine, :config, :info, :warning, :severe, or :off, :all
569
+ #
570
+ # @return [Symbol] the current log level
571
+ #
572
+
573
+ def log_level
574
+ Celerity::Util.logger_for('com.gargoylesoftware.htmlunit').level.to_s.downcase.to_sym
575
+ end
576
+
577
+ #
578
+ # Set Java log level (default is :warning, can be any of :all, :finest, :finer, :fine, :config, :info, :warning, :severe, :off)
579
+ #
580
+ # @param [Symbol] level The new log level.
581
+ #
582
+
583
+ def log_level=(level)
584
+ log_level = java.util.logging.Level.const_get(level.to_s.upcase)
585
+
586
+ [ 'com.gargoylesoftware.htmlunit',
587
+ 'com.gargoylesoftware.htmlunit.html',
588
+ 'com.gargoylesoftware.htmlunit.javascript',
589
+ 'org.apache.commons.httpclient'
590
+ ].each { |package| Celerity::Util.logger_for(package).level = log_level }
591
+
592
+ level
593
+ end
594
+
595
+ #
596
+ # Checks if we have a page currently loaded.
597
+ # @return [true, false]
598
+ #
599
+
600
+ def exist?
601
+ !!@page
602
+ end
603
+ alias_method :exists?, :exist?
604
+
605
+ #
606
+ # Turn on/off javascript exceptions
607
+ #
608
+ # @param [Bool]
609
+ #
610
+
611
+ def javascript_exceptions=(bool)
612
+ @webclient.throwExceptionOnScriptError = bool
613
+ end
614
+
615
+ def javascript_exceptions
616
+ @webclient.throwExceptionOnScriptError
617
+ end
618
+
619
+ #
620
+ # Turn on/off status code exceptions
621
+ #
622
+ # @param [Bool]
623
+ #
624
+
625
+ def status_code_exceptions=(bool)
626
+ @webclient.throwExceptionOnFailingStatusCode = bool
627
+ end
628
+
629
+ def status_code_exceptions
630
+ @webclient.throwExceptionOnFailingStatusCode
631
+ end
632
+
633
+ #
634
+ # Turn on/off CSS loading
635
+ #
636
+ # @param [Bool]
637
+ #
638
+
639
+ def css=(bool)
640
+ @webclient.cssEnabled = bool
641
+ end
642
+
643
+ def css
644
+ @webclient.cssEnabled
645
+ end
646
+
647
+ #
648
+ # Turn on/off secure SSL
649
+ #
650
+ # @param [Bool]
651
+ #
652
+
653
+ def secure_ssl=(bool)
654
+ @webclient.useInsecureSSL = !bool
655
+ end
656
+
657
+ def secure_ssl
658
+ !@webclient.useInsecureSSL
659
+ end
660
+
661
+ #
662
+ # Sets the current page object for the browser
663
+ #
664
+ # @param [HtmlUnit::HtmlPage] value The page to set.
665
+ # @api private
666
+ #
667
+
668
+ def page=(value)
669
+ @last_url = url() if exist?
670
+ @page = value
671
+
672
+ if @page.respond_to?("getDocumentElement")
673
+ @object = @page.getDocumentElement
674
+ elsif @page.is_a? HtmlUnit::UnexpectedPage
675
+ raise UnexpectedPageException, @page.getWebResponse.getContentType
676
+ end
677
+
678
+ render unless @viewer == DefaultViewer
679
+ run_error_checks
680
+
681
+ value
682
+ end
683
+
684
+ #
685
+ # Check that we have a @page object.
686
+ #
687
+ # @raise [Celerity::Exception::UnknownObjectException] if no page is loaded.
688
+ # @api private
689
+ #
690
+
691
+ def assert_exists
692
+ raise UnknownObjectException, "no page loaded" unless exist?
693
+ end
694
+
695
+ #
696
+ # Returns the element that currently has the focus (Celerity only)
697
+ #
698
+
699
+ def focused_element
700
+ element_from_dom_node(page.getFocusedElement())
701
+ end
702
+
703
+ private
704
+
705
+ #
706
+ # Runs the all the checker procs added by +add_checker+
707
+ #
708
+ # @see add_checker
709
+ # @api private
710
+ #
711
+
712
+ def run_error_checks
713
+ @error_checkers.each { |e| e[self] }
714
+ end
715
+
716
+ #
717
+ # Configure the webclient according to the options given to #new.
718
+ # @see initialize
719
+ #
720
+
721
+ def setup_webclient(opts)
722
+ browser = (opts.delete(:browser) || :firefox).to_sym
723
+
724
+ case browser
725
+ when :firefox, :ff
726
+ browser_version = ::HtmlUnit::BrowserVersion::FIREFOX_2
727
+ when :internet_explorer, :ie
728
+ browser_version = ::HtmlUnit::BrowserVersion::INTERNET_EXPLORER_7
729
+ else
730
+ raise ArgumentError, "unknown browser: #{browser.inspect}"
731
+ end
732
+
733
+ if ua = opts.delete(:user_agent)
734
+ browser_version.setUserAgent(ua)
735
+ end
736
+
737
+ if proxy = opts.delete(:proxy)
738
+ phost, pport = proxy.split(":")
739
+ @webclient = ::HtmlUnit::WebClient.new(browser_version, phost, pport.to_i)
740
+ else
741
+ @webclient = ::HtmlUnit::WebClient.new(browser_version)
742
+ end
743
+
744
+ self.javascript_exceptions = false unless opts.delete(:javascript_exceptions)
745
+ self.status_code_exceptions = false unless opts.delete(:status_code_exceptions)
746
+ self.css = false unless opts.delete(:css)
747
+ self.secure_ssl = opts.delete(:secure_ssl) == false
748
+ @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new) if opts.delete(:resynchronize)
749
+ end
750
+
751
+ #
752
+ # This *should* be unneccessary, but sometimes the page we get from the
753
+ # window is different (ie. a different object) from our current @page
754
+ # (Used by #wait_while and #wait_until)
755
+ #
756
+
757
+ def refresh_page_from_window
758
+ new_page = @page.getEnclosingWindow.getEnclosedPage
759
+
760
+ if new_page && (new_page != @page)
761
+ self.page = new_page
762
+ else
763
+ Log.debug "unneccessary refresh"
764
+ end
765
+ end
766
+
767
+ #
768
+ # Render the current page on the viewer.
769
+ # @api private
770
+ #
771
+
772
+ def render
773
+ @viewer.render_html(self.send(@render_type), url)
774
+ rescue DRb::DRbConnError, Errno::ECONNREFUSED => e
775
+ @viewer = DefaultViewer
776
+ end
777
+
778
+ #
779
+ # Check if we have a viewer available on druby://127.0.0.1:6429
780
+ # @api private
781
+ #
782
+
783
+ def find_viewer
784
+ # needed to avoid DRb raising and rescuing lots exceptions
785
+ DRb.start_service unless DRb.primary_server
786
+
787
+ viewer = DRbObject.new_with_uri("druby://127.0.0.1:6429")
788
+ if viewer.respond_to?(:render_html)
789
+ @viewer = viewer
790
+ else
791
+ @viewer = DefaultViewer
792
+ end
793
+ rescue DRb::DRbConnError, Errno::ECONNREFUSED
794
+ @viewer = DefaultViewer
795
+ end
796
+
797
+ #
798
+ # Convert the given HtmlUnit object to a Celerity object
799
+ #
800
+
801
+ def element_from_dom_node(obj)
802
+ element_class = Celerity::Util.htmlunit2celerity(obj.class) || Element
803
+ element_class.new(self, :object, obj)
804
+ end
805
+
806
+ def listener
807
+ @listener ||= Celerity::Listener.new(@webclient)
808
+ end
809
+
810
+ end # Browser
811
+ end # Celerity