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