oki-celerity 0.8.1.dev

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 (77) hide show
  1. data/HISTORY +111 -0
  2. data/LICENSE +621 -0
  3. data/README.rdoc +80 -0
  4. data/Rakefile +11 -0
  5. data/VERSION.yml +5 -0
  6. data/celerity.gemspec +126 -0
  7. data/lib/celerity/browser.rb +908 -0
  8. data/lib/celerity/clickable_element.rb +73 -0
  9. data/lib/celerity/collections.rb +164 -0
  10. data/lib/celerity/container.rb +800 -0
  11. data/lib/celerity/default_viewer.rb +14 -0
  12. data/lib/celerity/disabled_element.rb +40 -0
  13. data/lib/celerity/element.rb +311 -0
  14. data/lib/celerity/element_collection.rb +107 -0
  15. data/lib/celerity/element_locator.rb +164 -0
  16. data/lib/celerity/elements/button.rb +54 -0
  17. data/lib/celerity/elements/file_field.rb +29 -0
  18. data/lib/celerity/elements/form.rb +22 -0
  19. data/lib/celerity/elements/frame.rb +86 -0
  20. data/lib/celerity/elements/image.rb +89 -0
  21. data/lib/celerity/elements/label.rb +16 -0
  22. data/lib/celerity/elements/link.rb +43 -0
  23. data/lib/celerity/elements/meta.rb +14 -0
  24. data/lib/celerity/elements/non_control_elements.rb +124 -0
  25. data/lib/celerity/elements/option.rb +38 -0
  26. data/lib/celerity/elements/radio_check.rb +114 -0
  27. data/lib/celerity/elements/select_list.rb +146 -0
  28. data/lib/celerity/elements/table.rb +153 -0
  29. data/lib/celerity/elements/table_cell.rb +36 -0
  30. data/lib/celerity/elements/table_elements.rb +42 -0
  31. data/lib/celerity/elements/table_row.rb +49 -0
  32. data/lib/celerity/elements/text_field.rb +168 -0
  33. data/lib/celerity/exception.rb +83 -0
  34. data/lib/celerity/htmlunit/apache-mime4j-0.6.jar +0 -0
  35. data/lib/celerity/htmlunit/commons-codec-1.4.jar +0 -0
  36. data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
  37. data/lib/celerity/htmlunit/commons-io-1.4.jar +0 -0
  38. data/lib/celerity/htmlunit/commons-lang-2.5.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.9-SNAPSHOT.jar +0 -0
  42. data/lib/celerity/htmlunit/htmlunit-core-js-2.8.jar +0 -0
  43. data/lib/celerity/htmlunit/httpclient-4.0.1.jar +0 -0
  44. data/lib/celerity/htmlunit/httpcore-4.0.1.jar +0 -0
  45. data/lib/celerity/htmlunit/httpmime-4.0.1.jar +0 -0
  46. data/lib/celerity/htmlunit/nekohtml-1.9.14.jar +0 -0
  47. data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
  48. data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
  49. data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
  50. data/lib/celerity/htmlunit/xercesImpl-2.9.1.jar +0 -0
  51. data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
  52. data/lib/celerity/htmlunit.rb +64 -0
  53. data/lib/celerity/identifier.rb +28 -0
  54. data/lib/celerity/ignoring_web_connection.rb +15 -0
  55. data/lib/celerity/input_element.rb +25 -0
  56. data/lib/celerity/javascript_debugger.rb +32 -0
  57. data/lib/celerity/listener.rb +143 -0
  58. data/lib/celerity/resources/no_viewer.png +0 -0
  59. data/lib/celerity/short_inspect.rb +20 -0
  60. data/lib/celerity/util.rb +129 -0
  61. data/lib/celerity/version.rb +3 -0
  62. data/lib/celerity/viewer_connection.rb +89 -0
  63. data/lib/celerity/watir_compatibility.rb +70 -0
  64. data/lib/celerity/xpath_support.rb +52 -0
  65. data/lib/celerity.rb +77 -0
  66. data/tasks/benchmark.rake +4 -0
  67. data/tasks/check.rake +24 -0
  68. data/tasks/clean.rake +3 -0
  69. data/tasks/fix.rake +25 -0
  70. data/tasks/jar.rake +55 -0
  71. data/tasks/jeweler.rake +28 -0
  72. data/tasks/rdoc.rake +4 -0
  73. data/tasks/snapshot.rake +25 -0
  74. data/tasks/spec.rake +26 -0
  75. data/tasks/website.rake +10 -0
  76. data/tasks/yard.rake +16 -0
  77. metadata +204 -0
@@ -0,0 +1,908 @@
1
+ module Celerity
2
+ class Browser
3
+ include Container
4
+ include XpathSupport
5
+
6
+ attr_accessor :page, :object, :charset, :headers
7
+ attr_reader :webclient, :viewer, :options
8
+
9
+ #
10
+ # Initialize a browser and go to the given URL
11
+ #
12
+ # @param [String] uri The URL to go to.
13
+ # @return [Celerity::Browser] instance.
14
+ #
15
+
16
+ def self.start(uri)
17
+ browser = new
18
+ browser.goto(uri)
19
+ browser
20
+ end
21
+
22
+ #
23
+ # Not implemented. Use ClickableElement#click_and_attach instead.
24
+ #
25
+
26
+ def self.attach(*args)
27
+ raise NotImplementedError, "use ClickableElement#click_and_attach instead"
28
+ end
29
+
30
+ #
31
+ # Creates a browser object.
32
+ #
33
+ # @see Celerity::Container for an introduction to the main API.
34
+ #
35
+ # @option opts :browser [:internet_explorer, :firefox, :firefox3] (:firefox3) Set the BrowserVersion used by HtmlUnit. Defaults to Firefox 3.
36
+ # @option opts :charset [String] ("UTF-8") Specify the charset that webclient will use for requests.
37
+ # @option opts :css [Boolean] (true) Enable/disable CSS. Enabled by default.
38
+ # @option opts :ignore_pattern [Regexp] See Browser#ignore_pattern=
39
+ # @option opts :javascript_enabled [Boolean] (true) Enable/disable JavaScript evaluation. Enabled by default.
40
+ # @option opts :javascript_exceptions [Boolean] (false) Raise exceptions on script errors. Disabled by default.
41
+ # @option opts :log_level [Symbol] (:warning) @see log_level=
42
+ # @option opts :proxy [String] (nil) Proxy server to use, in address:port format.
43
+ # @option opts :refresh_handler [:immediate, :waiting, :threaded] (:immediate) Set HtmlUnit's refresh handler.
44
+ # @option opts :render [:html, :xml] (:html) What DOM representation to send to connected viewers.
45
+ # @option opts :resynchronize [Boolean] (false) Use HtmlUnit::NicelyResynchronizingAjaxController to resynchronize Ajax calls.
46
+ # @option opts :secure_ssl [Boolean] (true) Enable/disable secure SSL. Enabled by default.
47
+ # @option opts :status_code_exceptions [Boolean] (false) Raise exceptions on failing status codes (404 etc.). Disabled by default.
48
+ # @option opts :user_agent [String] Override the User-Agent set by the :browser option
49
+ # @option opts :viewer [String, false] ("127.0.0.1:6429") Connect to a CelerityViewer if available.
50
+ #
51
+ # @return [Celerity::Browser] An instance of the browser.
52
+ #
53
+ # @api public
54
+ #
55
+
56
+ def initialize(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, 'html', 'xml']).include?(opts[:render])
62
+ raise ArgumentError, "expected one of #{render_types.inspect} for key :render"
63
+ end
64
+
65
+ @options = opts.dup # keep the unmodified version around as well
66
+ opts = opts.dup # we'll delete from opts, so dup to avoid side effects
67
+
68
+ @render_type = opts.delete(:render) || :html
69
+ @charset = opts.delete(:charset) || "UTF-8"
70
+ @headers = {}
71
+ @page = nil
72
+ @error_checkers = []
73
+ @browser = self # for Container#browser
74
+
75
+ setup_webclient opts
76
+ setup_viewer opts.delete(:viewer)
77
+
78
+ self.log_level = opts.delete(:log_level) || :off
79
+
80
+ raise ArgumentError, "unknown option #{opts.inspect}" unless opts.empty?
81
+ end
82
+
83
+ def inspect
84
+ short_inspect :exclude => %w[@webclient @browser @object @options @listener @event_listener]
85
+ end
86
+
87
+ #
88
+ # Goto the given URL
89
+ #
90
+ # @param [String] uri The url.
91
+ # @return [String] The url.
92
+ #
93
+
94
+ def goto(uri)
95
+ uri = "http://#{uri}" unless uri =~ %r{://}
96
+
97
+ puts "uri: #{uri}"
98
+
99
+ request = HtmlUnit::WebRequestSettings.new(::Java::JavaNet::URL.new(uri))
100
+ request.setCharset(@charset)
101
+
102
+ self.headers.each_pair do |name,value|
103
+ puts "#{name}: #{value}"
104
+ request.setAdditionalHeader(name,value)
105
+ end
106
+
107
+ rescue_status_code_exception do
108
+ self.page = @webclient.getPage(request)
109
+ end
110
+
111
+ url()
112
+ end
113
+
114
+ #
115
+ # Set the credentials used for basic HTTP authentication. (Celerity only)
116
+ #
117
+ # Example:
118
+ # browser.credentials = "username:password"
119
+ #
120
+ # @param [String] A string with username / password, separated by a colon
121
+ #
122
+
123
+ def credentials=(string)
124
+ user, pass = string.split(":")
125
+ dcp = HtmlUnit::DefaultCredentialsProvider.new
126
+ dcp.addCredentials(user, pass)
127
+ @webclient.setCredentialsProvider(dcp)
128
+ end
129
+
130
+ #
131
+ # Unsets the current page / closes all windows
132
+ #
133
+
134
+ def close
135
+ @page = nil
136
+ @webclient.closeAllWindows
137
+ @viewer.close
138
+ end
139
+
140
+ #
141
+ # @return [String] the URL of the current page
142
+ #
143
+
144
+ def url
145
+ assert_exists
146
+ @page.getWebResponse.getWebRequest.getUrl.toString
147
+ end
148
+
149
+ #
150
+ # @return [String] the title of the current page
151
+ #
152
+
153
+ def title
154
+ @page ? @page.getTitleText : ''
155
+ end
156
+
157
+ #
158
+ # @return [String] the value of window.status
159
+ #
160
+
161
+ def status
162
+ execute_script "window.status" # avoid the listener overhead
163
+ end
164
+
165
+ #
166
+ # @return [String] the HTML content of the current page
167
+ #
168
+
169
+ def html
170
+ return '' unless @page
171
+
172
+ @page.getWebResponse.getContentAsString(@charset)
173
+ end
174
+
175
+ #
176
+ # @return [String] the XML representation of the DOM
177
+ #
178
+
179
+ def xml
180
+ return '' unless @page
181
+ return @page.asXml if @page.respond_to?(:asXml)
182
+ return text # fallback to text (for exampel for "plain/text" pages)
183
+ end
184
+
185
+ #
186
+ # @return [String] a text representation of the current page
187
+ #
188
+
189
+ def text
190
+ return '' unless @page
191
+
192
+ if @page.respond_to?(:getContent)
193
+ @page.getContent.strip
194
+ elsif @page.respond_to?(:getDocumentElement) && doc = @page.getDocumentElement
195
+ doc.asText.strip
196
+ else
197
+ ''
198
+ end
199
+ end
200
+
201
+ #
202
+ # @return [Hash] response headers as a hash
203
+ #
204
+
205
+ def response_headers
206
+ return {} unless @page
207
+
208
+ Hash[*@page.getWebResponse.getResponseHeaders.map { |obj| [obj.name, obj.value] }.flatten]
209
+ end
210
+
211
+ #
212
+ # @return [Fixnum] status code of the last request
213
+ #
214
+
215
+ def status_code
216
+ @page.getWebResponse.getStatusCode
217
+ end
218
+
219
+ #
220
+ # @return [String] content-type as in 'text/html'
221
+ #
222
+
223
+ def content_type
224
+ return '' unless @page
225
+
226
+ @page.getWebResponse.getContentType
227
+ end
228
+
229
+ #
230
+ # @return [IO, nil] page contents as an IO, returns nil if no page is loaded.
231
+ #
232
+
233
+ def io
234
+ return nil unless @page
235
+
236
+ @page.getWebResponse.getContentAsStream.to_io
237
+ end
238
+
239
+ #
240
+ # Check if the current page contains the given text.
241
+ #
242
+ # @param [String, Regexp] expected_text The text to look for.
243
+ # @return [Numeric, nil] The index of the matched text, or nil if it isn't found.
244
+ # @raise [TypeError]
245
+ #
246
+
247
+ def contains_text(expected_text)
248
+ return nil unless exist?
249
+ super
250
+ end
251
+
252
+ #
253
+ # @return [HtmlUnit::HtmlHtml] the underlying HtmlUnit document.
254
+ #
255
+
256
+ def document
257
+ @object
258
+ end
259
+
260
+ #
261
+ # Goto back one history item
262
+ # @return [String] The url of the resulting page.
263
+ #
264
+
265
+ def back
266
+ @webclient.getCurrentWindow.getHistory.back
267
+ refresh_page_from_window
268
+
269
+ url
270
+ end
271
+
272
+ #
273
+ # Go forward one history item
274
+ # @return [String] The url of the resulting page.
275
+ #
276
+
277
+ def forward
278
+ @webclient.getCurrentWindow.getHistory.forward
279
+ refresh_page_from_window
280
+
281
+ url
282
+ end
283
+
284
+ #
285
+ # Wait for javascript jobs to finish
286
+ #
287
+
288
+ def wait
289
+ assert_exists
290
+ @webclient.waitForBackgroundJavaScript(10000);
291
+ end
292
+
293
+ #
294
+ # Refresh the current page
295
+ #
296
+
297
+ def refresh
298
+ assert_exists
299
+ @page.refresh
300
+ end
301
+
302
+ #
303
+ # Clears all cookies. (Celerity only)
304
+ #
305
+
306
+ def clear_cookies
307
+ @webclient.getCookieManager.clearCookies
308
+ end
309
+
310
+ #
311
+ # Clears the cache of "compiled JavaScript files and parsed CSS snippets"
312
+ #
313
+
314
+ def clear_cache
315
+ @webclient.cache.clear
316
+ end
317
+
318
+ #
319
+ # Get the cookies for this session. (Celerity only)
320
+ #
321
+ # @return [Hash<domain, Hash<name, value>>]
322
+ #
323
+
324
+ def cookies
325
+ result = Hash.new { |hash, key| hash[key] = {} }
326
+
327
+ cookies = @webclient.getCookieManager.getCookies
328
+ cookies.each do |cookie|
329
+ result[cookie.getDomain][cookie.getName] = cookie.getValue
330
+ end
331
+
332
+ result
333
+ end
334
+
335
+ #
336
+ # Add a cookie with the given parameters (Celerity only)
337
+ #
338
+ # @param [String] domain
339
+ # @param [String] name
340
+ # @param [String] value
341
+ #
342
+ # @option opts :path [String] ("/") A path
343
+ # @option opts :expires [Time] (1 day from now) An expiration date
344
+ # @option opts :secure [Boolean] (false)
345
+ #
346
+
347
+ def add_cookie(domain, name, value, opts = {})
348
+ path = opts.delete(:path) || "/"
349
+ max_age = opts.delete(:expires) || (Time.now + 60*60*24) # not sure if this is correct
350
+ secure = opts.delete(:secure) || false
351
+
352
+ raise(ArgumentError, "unknown option: #{opts.inspect}") unless opts.empty?
353
+
354
+ cookie = HtmlUnit::Util::Cookie.new(domain, name, value, path, max_age, secure)
355
+ @webclient.getCookieManager.addCookie cookie
356
+
357
+ cookie
358
+ end
359
+
360
+ #
361
+ # Remove the cookie with the given domain and name (Celerity only)
362
+ #
363
+ # @param [String] domain
364
+ # @param [String] name
365
+ #
366
+ # @raise [CookieNotFoundError] if the cookie doesn't exist
367
+ #
368
+
369
+ def remove_cookie(domain, name)
370
+ cm = @webclient.getCookieManager
371
+ cookie = cm.getCookies.find { |c| c.getDomain == domain && c.getName == name }
372
+
373
+ if cookie.nil?
374
+ raise CookieNotFoundError, "no cookie with domain #{domain.inspect} and name #{name.inspect}"
375
+ end
376
+
377
+ cm.removeCookie(cookie)
378
+ end
379
+
380
+ #
381
+ # Execute the given JavaScript on the current page.
382
+ # @return [Object] The resulting Object
383
+ #
384
+
385
+ def execute_script(source)
386
+ assert_exists
387
+ @page.executeJavaScript(source.to_s).getJavaScriptResult
388
+ end
389
+
390
+ # experimental - should be removed?
391
+ def send_keys(keys)
392
+ keys = keys.gsub(/\s*/, '').scan(/((?:\{[A-Z]+?\})|.)/u).flatten
393
+ keys.each do |key|
394
+ element = @page.getFocusedElement
395
+ case key
396
+ when "{TAB}"
397
+ @page.tabToNextElement
398
+ when /\w/
399
+ element.type(key)
400
+ else
401
+ raise NotImplementedError
402
+ end
403
+ end
404
+ end
405
+
406
+ #
407
+ # Wait until the given block evaluates to true (Celerity only)
408
+ #
409
+ # @param [Fixnum] timeout Number of seconds to wait before timing out (default: 30).
410
+ # @yieldparam [Celerity::Browser] browser The browser instance.
411
+ # @see Celerity::Browser#resynchronized
412
+ #
413
+
414
+ def wait_until(timeout = 30, &block)
415
+ returned = nil
416
+
417
+ Timeout.timeout(timeout) do
418
+ until returned = yield(self)
419
+ refresh_page_from_window
420
+ sleep 0.1
421
+ end
422
+ end
423
+
424
+ returned
425
+ end
426
+
427
+ #
428
+ # Wait while the given block evaluates to true (Celerity only)
429
+ #
430
+ # @param [Fixnum] timeout Number of seconds to wait before timing out (default: 30).
431
+ # @yieldparam [Celerity::Browser] browser The browser instance.
432
+ # @see Celerity::Browser#resynchronized
433
+ #
434
+
435
+ def wait_while(timeout = 30, &block)
436
+ returned = nil
437
+
438
+ Timeout.timeout(timeout) do
439
+ while returned = yield(self)
440
+ refresh_page_from_window
441
+ sleep 0.1
442
+ end
443
+ end
444
+
445
+ returned
446
+ end
447
+
448
+ #
449
+ # Allows you to temporarily switch to HtmlUnit's NicelyResynchronizingAjaxController
450
+ # to resynchronize ajax calls.
451
+ #
452
+ # @browser.resynchronized do |b|
453
+ # b.link(:id, 'trigger_ajax_call').click
454
+ # end
455
+ #
456
+ # @yieldparam [Celerity::Browser] browser The current browser object.
457
+ # @see Celerity::Browser#new for how to configure the browser to always use this.
458
+ #
459
+
460
+ def resynchronized(&block)
461
+ old_controller = @webclient.ajaxController
462
+ @webclient.setAjaxController(::HtmlUnit::NicelyResynchronizingAjaxController.new)
463
+ yield self
464
+ @webclient.setAjaxController(old_controller)
465
+ end
466
+
467
+ #
468
+ # Allows you to temporarliy switch to HtmlUnit's default AjaxController, so
469
+ # ajax calls are performed asynchronously. This is useful if you have created
470
+ # the Browser with :resynchronize => true, but want to switch it off temporarily.
471
+ #
472
+ # @yieldparam [Celerity::Browser] browser The current browser object.
473
+ # @see Celerity::Browser#new
474
+ #
475
+
476
+ def asynchronized(&block)
477
+ old_controller = @webclient.ajaxController
478
+ @webclient.setAjaxController(::HtmlUnit::AjaxController.new)
479
+ yield self
480
+ @webclient.setAjaxController(old_controller)
481
+ end
482
+
483
+ #
484
+ # Start or stop HtmlUnit's DebuggingWebConnection. (Celerity only)
485
+ # The output will go to /tmp/«name»
486
+ #
487
+ # @param [String] name directory name
488
+ # @param [block] blk block to execute
489
+ #
490
+
491
+ def debug_web_connection(name, &blk)
492
+ old_wc = @webclient.getWebConnection
493
+
494
+ @webclient.setWebConnection HtmlUnit::Util::DebuggingWebConnection.new(old_wc, name)
495
+ res = yield
496
+ @webclient.setWebConnection old_wc
497
+
498
+ res
499
+ end
500
+
501
+ def trace_javascript(debugger_klass = Celerity::JavascriptDebugger, &blk)
502
+ context_factory = @webclient.getJavaScriptEngine.getContextFactory
503
+ context_factory.setDebugger debugger_klass.new
504
+ yield
505
+ context_factory.setDebugger nil
506
+ end
507
+
508
+ #
509
+ # Add a listener block for one of the available types. (Celerity only)
510
+ # Types map to HtmlUnit interfaces like this:
511
+ #
512
+ # :status => StatusHandler
513
+ # :alert => AlertHandler ( window.alert() )
514
+ # :web_window_event => WebWindowListener
515
+ # :html_parser => HTMLParserListener
516
+ # :incorrectness => IncorrectnessListener
517
+ # :confirm => ConfirmHandler ( window.confirm() )
518
+ # :prompt => PromptHandler ( window.prompt() )
519
+ #
520
+ # Examples:
521
+ #
522
+ # browser.add_listener(:status) { |page, message| ... }
523
+ # browser.add_listener(:alert) { |page, message| ... }
524
+ # browser.add_listener(:web_window_event) { |web_window_event| ... }
525
+ # browser.add_listener(:html_parser) { |message, url, line, column, key| ... }
526
+ # browser.add_listener(:incorrectness) { |message, origin| ... }
527
+ # browser.add_listener(:confirm) { |page, message| ...; true }
528
+ # browser.add_listener(:prompt) { |page, message| ... }
529
+ #
530
+ #
531
+ # @param [Symbol] type One of the above symbols.
532
+ # @param [Proc] block A block to be executed for events of this type.
533
+ #
534
+
535
+ def add_listener(type, &block)
536
+ listener.add_listener(type, &block)
537
+ end
538
+
539
+ def remove_listener(type, block)
540
+ listener.remove_listener(type, block)
541
+ end
542
+
543
+ #
544
+ # Specify a boolean value to click either 'OK' or 'Cancel' in any confirm
545
+ # dialogs that might show up during the duration of the given block.
546
+ #
547
+ # (Celerity only)
548
+ #
549
+ # @param [Boolean] bool true to click 'OK', false to click 'cancel'
550
+ # @param [Proc] block A block that will trigger the confirm() call(s).
551
+ #
552
+
553
+ def confirm(bool, &block)
554
+ blk = lambda { bool }
555
+
556
+ listener.add_listener(:confirm, &blk)
557
+ yield
558
+ listener.remove_listener(:confirm, blk)
559
+ end
560
+
561
+ #
562
+ # Add a 'checker' proc that will be run on every page load
563
+ #
564
+ # @param [Proc] checker The proc to be run (can also be given as a block)
565
+ # @yieldparam [Celerity::Browser] browser The current browser object.
566
+ # @raise [ArgumentError] if no Proc or block was given.
567
+ #
568
+
569
+ def add_checker(checker = nil, &block)
570
+ if block_given?
571
+ @error_checkers << block
572
+ elsif Proc === checker
573
+ @error_checkers << checker
574
+ else
575
+ raise ArgumentError, "argument must be a Proc or block"
576
+ end
577
+ end
578
+
579
+ #
580
+ # Remove the given checker from the list of checkers
581
+ # @param [Proc] checker The Proc to disable.
582
+ #
583
+
584
+ def disable_checker(checker)
585
+ @error_checkers.delete(checker)
586
+ end
587
+
588
+ #
589
+ # :finest, :finer, :fine, :config, :info, :warning, :severe, or :off, :all
590
+ #
591
+ # @return [Symbol] the current log level
592
+ #
593
+
594
+ def log_level
595
+ Celerity::Util.logger_for('com.gargoylesoftware.htmlunit').level.to_s.downcase.to_sym
596
+ end
597
+
598
+ #
599
+ # Set Java log level (default is :warning, can be any of :all, :finest, :finer, :fine, :config, :info, :warning, :severe, :off)
600
+ #
601
+ # @param [Symbol] level The new log level.
602
+ #
603
+
604
+ def log_level=(level)
605
+ log_level = java.util.logging.Level.const_get(level.to_s.upcase)
606
+
607
+ [ 'com.gargoylesoftware.htmlunit',
608
+ 'com.gargoylesoftware.htmlunit.html',
609
+ 'com.gargoylesoftware.htmlunit.javascript',
610
+ 'org.apache.commons.httpclient'
611
+ ].each { |package| Celerity::Util.logger_for(package).level = log_level }
612
+
613
+ level
614
+ end
615
+
616
+ #
617
+ # If a request is made to an URL that matches the pattern set here, Celerity
618
+ # will ignore the request and return an empty page with content type "text/html" instead.
619
+ #
620
+ # This is useful to block unwanted requests (like ads/banners).
621
+ #
622
+
623
+ def ignore_pattern=(regexp)
624
+ unless regexp.kind_of?(Regexp)
625
+ raise TypeError, "expected Regexp, got #{regexp.inspect}:#{regexp.class}"
626
+ end
627
+
628
+ Celerity::IgnoringWebConnection.new(@webclient, regexp)
629
+ end
630
+
631
+ #
632
+ # Checks if we have a page currently loaded.
633
+ # @return [true, false]
634
+ #
635
+
636
+ def exist?
637
+ !!@page
638
+ end
639
+ alias_method :exists?, :exist?
640
+
641
+ #
642
+ # Turn on/off javascript exceptions
643
+ #
644
+ # @param [Bool]
645
+ #
646
+
647
+ def javascript_exceptions=(bool)
648
+ @webclient.throwExceptionOnScriptError = bool
649
+ end
650
+
651
+ def javascript_exceptions
652
+ @webclient.throwExceptionOnScriptError
653
+ end
654
+
655
+ #
656
+ # Turn on/off status code exceptions
657
+ #
658
+ # @param [Bool]
659
+ #
660
+
661
+ def status_code_exceptions=(bool)
662
+ @webclient.throwExceptionOnFailingStatusCode = bool
663
+ end
664
+
665
+ def status_code_exceptions
666
+ @webclient.throwExceptionOnFailingStatusCode
667
+ end
668
+
669
+ #
670
+ # Turn on/off CSS loading
671
+ #
672
+ # @param [Bool]
673
+ #
674
+
675
+ def css=(bool)
676
+ @webclient.cssEnabled = bool
677
+ end
678
+
679
+ def css
680
+ @webclient.cssEnabled
681
+ end
682
+
683
+ def refresh_handler=(symbol)
684
+ handler = case symbol
685
+ when :waiting
686
+ HtmlUnit::WaitingRefreshHandler.new
687
+ when :threaded
688
+ HtmlUnit::ThreadedRefreshHandler.new
689
+ when :immediate
690
+ HtmlUnit::ImmediateRefreshHandler.new
691
+ else
692
+ raise ArgumentError, "expected :waiting, :threaded or :immediate"
693
+ end
694
+
695
+ @webclient.setRefreshHandler handler
696
+ end
697
+
698
+ #
699
+ # Turn on/off secure SSL
700
+ #
701
+ # @param [Bool]
702
+ #
703
+
704
+ def secure_ssl=(bool)
705
+ @webclient.useInsecureSSL = !bool
706
+ end
707
+
708
+ #
709
+ # Turn on/off JavaScript execution
710
+ #
711
+ # @param [Bool]
712
+ #
713
+
714
+ def javascript_enabled=(bool)
715
+ @webclient.setJavaScriptEnabled(bool)
716
+ end
717
+
718
+ def javascript_enabled
719
+ @webclient.isJavaScriptEnabled
720
+ end
721
+
722
+ #
723
+ # Open the JavaScript debugger GUI
724
+ #
725
+
726
+ def visual_debugger
727
+ HtmlUnit::Util::WebClientUtils.attachVisualDebugger @webclient
728
+ end
729
+
730
+ #
731
+ # Sets the current page object for the browser
732
+ #
733
+ # @param [HtmlUnit::HtmlPage] value The page to set.
734
+ # @api private
735
+ #
736
+
737
+ def page=(value)
738
+ return if @page == value
739
+ @page = value
740
+
741
+ if @page.respond_to?("getDocumentElement")
742
+ @object = @page.getDocumentElement || @object
743
+ elsif @page.is_a? HtmlUnit::UnexpectedPage
744
+ raise UnexpectedPageException, @page.getWebResponse.getContentType
745
+ end
746
+
747
+ render unless @viewer == DefaultViewer
748
+ run_error_checks
749
+
750
+ value
751
+ end
752
+
753
+ #
754
+ # Check that we have a @page object.
755
+ #
756
+ # @raise [UnknownObjectException] if no page is loaded.
757
+ # @api private
758
+ #
759
+
760
+ def assert_exists
761
+ raise UnknownObjectException, "no page loaded" unless exist?
762
+ end
763
+
764
+ #
765
+ # Returns the element that currently has the focus (Celerity only)
766
+ #
767
+
768
+ def focused_element
769
+ element_from_dom_node(page.getFocusedElement())
770
+ end
771
+
772
+ #
773
+ # Enable Celerity's internal WebWindowEventListener
774
+ #
775
+ # @api private
776
+ #
777
+
778
+ def enable_event_listener
779
+ @event_listener ||= lambda do |event|
780
+ self.page = @page ? @page.getEnclosingWindow.getEnclosedPage : event.getNewPage
781
+ end
782
+
783
+ listener.add_listener(:web_window_event, &@event_listener)
784
+ end
785
+
786
+ #
787
+ # Disable Celerity's internal WebWindowEventListener
788
+ #
789
+ # @api private
790
+ #
791
+
792
+ def disable_event_listener
793
+ listener.remove_listener(:web_window_event, @event_listener)
794
+
795
+ if block_given?
796
+ result = yield
797
+ enable_event_listener
798
+
799
+ result
800
+ end
801
+ end
802
+
803
+ private
804
+
805
+ #
806
+ # Runs the all the checker procs added by +add_checker+
807
+ #
808
+ # @see add_checker
809
+ # @api private
810
+ #
811
+
812
+ def run_error_checks
813
+ @error_checkers.each { |e| e[self] }
814
+ end
815
+
816
+ #
817
+ # Configure the webclient according to the options given to #new.
818
+ # @see initialize
819
+ #
820
+
821
+ def setup_webclient(opts)
822
+ browser = (opts.delete(:browser) || :firefox3).to_sym
823
+
824
+ browser_version = case browser
825
+ when :firefox, :ff, :ff2
826
+ ::HtmlUnit::BrowserVersion::FIREFOX_2
827
+ when :firefox3, :ff3
828
+ ::HtmlUnit::BrowserVersion::FIREFOX_3
829
+ when :internet_explorer, :ie
830
+ ::HtmlUnit::BrowserVersion::INTERNET_EXPLORER_7
831
+ else
832
+ raise ArgumentError, "unknown browser: #{browser.inspect}"
833
+ end
834
+
835
+ if ua = opts.delete(:user_agent)
836
+ browser_version.setUserAgent(ua)
837
+ end
838
+
839
+ @webclient = if proxy = opts.delete(:proxy)
840
+ phost, pport = proxy.split(":")
841
+ ::HtmlUnit::WebClient.new(browser_version, phost, pport.to_i)
842
+ else
843
+ ::HtmlUnit::WebClient.new(browser_version)
844
+ end
845
+
846
+ self.javascript_exceptions = false unless opts.delete(:javascript_exceptions)
847
+ self.status_code_exceptions = false unless opts.delete(:status_code_exceptions)
848
+ self.css = opts.delete(:css) if opts[:css]
849
+ self.javascript_enabled = opts.delete(:javascript_enabled) != false
850
+ self.secure_ssl = opts.delete(:secure_ssl) != false
851
+ self.ignore_pattern = opts.delete(:ignore_pattern) if opts[:ignore_pattern]
852
+ self.refresh_handler = opts.delete(:refresh_handler) if opts[:refresh_handler]
853
+
854
+ if opts.delete(:resynchronize)
855
+ controller = ::HtmlUnit::NicelyResynchronizingAjaxController.new
856
+ @webclient.setAjaxController controller
857
+ end
858
+
859
+ enable_event_listener
860
+ end
861
+
862
+ def setup_viewer(option)
863
+ @viewer = DefaultViewer
864
+ return if option == false
865
+
866
+ host_string = option.kind_of?(String) ? option : "127.0.0.1:6429"
867
+ host, port = host_string.split(":")
868
+
869
+ if viewer = ViewerConnection.create(host, port.to_i)
870
+ @viewer = viewer
871
+ end
872
+ rescue Errno::ECONNREFUSED, SocketError => e
873
+ nil
874
+ end
875
+
876
+ #
877
+ # This *should* be unneccessary, but sometimes the page we get from the
878
+ # window is different (ie. a different object) from our current @page
879
+ # (Used by #wait_while and #wait_until)
880
+ #
881
+
882
+ def refresh_page_from_window
883
+ new_page = @page.getEnclosingWindow.getEnclosedPage
884
+
885
+ if new_page && (new_page != @page)
886
+ self.page = new_page
887
+ else
888
+ Log.debug "unneccessary refresh"
889
+ end
890
+ end
891
+
892
+ #
893
+ # Render the current page on the connected viewer.
894
+ # @api private
895
+ #
896
+
897
+ def render
898
+ @viewer.render_html(self.send(@render_type), url)
899
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE
900
+ @viewer = DefaultViewer
901
+ end
902
+
903
+ def listener
904
+ @listener ||= Celerity::Listener.new(@webclient)
905
+ end
906
+
907
+ end # Browser
908
+ end # Celerity