oki-celerity 0.8.1.dev

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