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