walidhalabi-celerity 0.0.6.11

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