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
|
Binary file
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Celerity
|
|
2
|
+
module ShortInspect
|
|
3
|
+
|
|
4
|
+
def short_inspect(opts)
|
|
5
|
+
if excluded_ivars = opts[:exclude]
|
|
6
|
+
if is_ruby19
|
|
7
|
+
excluded_ivars.map! { |ivar| ivar.to_sym }
|
|
8
|
+
end
|
|
9
|
+
ivars = (instance_variables - excluded_ivars)
|
|
10
|
+
elsif included_ivars = opts[:include]
|
|
11
|
+
if is_ruby19
|
|
12
|
+
included_ivars.map! { |ivar| ivar.to_sym }
|
|
13
|
+
end
|
|
14
|
+
ivars = included_ivars
|
|
15
|
+
else
|
|
16
|
+
raise ArgumentError, "unknown arg: #{opts.inspect}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
ivars.map! { |ivar| "#{ivar}=#{instance_variable_get(ivar).inspect}" }
|
|
20
|
+
'#<%s:0x%s %s>' % [self.class.name, self.hash.to_s(16), ivars.join(" ")]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
def is_ruby19
|
|
25
|
+
RUBY_VERSION >= "1.9"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module Celerity
|
|
2
|
+
module Util
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
HtmlUnit2CelerityElement = {
|
|
6
|
+
HtmlUnit::Html::HtmlAnchor => Celerity::Link,
|
|
7
|
+
HtmlUnit::Html::HtmlArea => Celerity::Area,
|
|
8
|
+
HtmlUnit::Html::HtmlButton => Celerity::Button,
|
|
9
|
+
HtmlUnit::Html::HtmlSubmitInput => Celerity::Button,
|
|
10
|
+
HtmlUnit::Html::HtmlResetInput => Celerity::Button,
|
|
11
|
+
HtmlUnit::Html::HtmlImageInput => Celerity::Button,
|
|
12
|
+
HtmlUnit::Html::HtmlButtonInput => Celerity::Button,
|
|
13
|
+
HtmlUnit::Html::HtmlCaption => Celerity::Button, # ?
|
|
14
|
+
HtmlUnit::Html::HtmlCheckBoxInput => Celerity::CheckBox,
|
|
15
|
+
HtmlUnit::Html::HtmlDefinitionDescription => Celerity::Dd,
|
|
16
|
+
HtmlUnit::Html::HtmlDefinitionList => Celerity::Dl,
|
|
17
|
+
HtmlUnit::Html::HtmlDefinitionTerm => Celerity::Dt,
|
|
18
|
+
HtmlUnit::Html::HtmlDeletedText => Celerity::Del,
|
|
19
|
+
HtmlUnit::Html::HtmlDivision => Celerity::Div,
|
|
20
|
+
HtmlUnit::Html::HtmlFileInput => Celerity::FileField,
|
|
21
|
+
HtmlUnit::Html::HtmlForm => Celerity::Form,
|
|
22
|
+
HtmlUnit::Html::HtmlFrame => Celerity::Frame,
|
|
23
|
+
HtmlUnit::Html::HtmlHeading1 => Celerity::H1,
|
|
24
|
+
HtmlUnit::Html::HtmlHeading2 => Celerity::H2,
|
|
25
|
+
HtmlUnit::Html::HtmlHeading3 => Celerity::H3,
|
|
26
|
+
HtmlUnit::Html::HtmlHeading4 => Celerity::H4,
|
|
27
|
+
HtmlUnit::Html::HtmlHeading5 => Celerity::H5,
|
|
28
|
+
HtmlUnit::Html::HtmlHeading6 => Celerity::H6,
|
|
29
|
+
HtmlUnit::Html::HtmlHiddenInput => Celerity::Hidden,
|
|
30
|
+
HtmlUnit::Html::HtmlImage => Celerity::Image,
|
|
31
|
+
HtmlUnit::Html::HtmlLabel => Celerity::Label,
|
|
32
|
+
HtmlUnit::Html::HtmlListItem => Celerity::Li,
|
|
33
|
+
HtmlUnit::Html::HtmlMap => Celerity::Map,
|
|
34
|
+
HtmlUnit::Html::HtmlOption => Celerity::Option,
|
|
35
|
+
HtmlUnit::Html::HtmlOrderedList => Celerity::Ol,
|
|
36
|
+
HtmlUnit::Html::HtmlParagraph => Celerity::P,
|
|
37
|
+
HtmlUnit::Html::HtmlPasswordInput => Celerity::TextField,
|
|
38
|
+
HtmlUnit::Html::HtmlPreformattedText => Celerity::Pre,
|
|
39
|
+
HtmlUnit::Html::HtmlRadioButtonInput => Celerity::Radio,
|
|
40
|
+
HtmlUnit::Html::HtmlSelect => Celerity::SelectList,
|
|
41
|
+
HtmlUnit::Html::HtmlSpan => Celerity::Span,
|
|
42
|
+
HtmlUnit::Html::HtmlTable => Celerity::Table,
|
|
43
|
+
HtmlUnit::Html::HtmlTableBody => Celerity::TableBody,
|
|
44
|
+
HtmlUnit::Html::HtmlTableCell => Celerity::TableCell,
|
|
45
|
+
HtmlUnit::Html::HtmlTableDataCell => Celerity::TableCell,
|
|
46
|
+
HtmlUnit::Html::HtmlTableFooter => Celerity::TableFooter,
|
|
47
|
+
HtmlUnit::Html::HtmlTableHeader => Celerity::TableHeader,
|
|
48
|
+
HtmlUnit::Html::HtmlTableRow => Celerity::TableRow,
|
|
49
|
+
HtmlUnit::Html::HtmlTextArea => Celerity::TextField,
|
|
50
|
+
HtmlUnit::Html::HtmlTextInput => Celerity::TextField,
|
|
51
|
+
HtmlUnit::Html::HtmlUnorderedList => Celerity::Ul,
|
|
52
|
+
HtmlUnit::Html::HtmlInsertedText => Celerity::Ins
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def htmlunit2celerity(klass)
|
|
56
|
+
HtmlUnit2CelerityElement[klass]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# HtmlUnit will recognize most common file types, but custom ones can be added here.
|
|
61
|
+
# Used for FileField uploads.
|
|
62
|
+
#
|
|
63
|
+
|
|
64
|
+
ContentTypes = {
|
|
65
|
+
".bmp" => "image/x-ms-bmp",
|
|
66
|
+
".doc" => "application/msword",
|
|
67
|
+
".odg" => "application/vnd.oasis.opendocument.graphics",
|
|
68
|
+
".odp" => "application/vnd.oasis.opendocument.presentation",
|
|
69
|
+
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
|
|
70
|
+
".odt" => "application/vnd.oasis.opendocument.text",
|
|
71
|
+
".ppt" => "application/vnd.ms-powerpoint",
|
|
72
|
+
".xls" => "application/vnd.ms-excel",
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
def content_type_for(path)
|
|
76
|
+
if ct = java.net.URLConnection.getFileNameMap.getContentTypeFor(path)
|
|
77
|
+
return ct
|
|
78
|
+
else
|
|
79
|
+
ContentTypes[File.extname(path).downcase]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def normalize_text(string)
|
|
84
|
+
string.gsub("\302\240", ' '). # non-breaking space 00A0
|
|
85
|
+
gsub(/\n|\t/, ''). # get rid of newlines/tabs
|
|
86
|
+
strip
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def logger_for(package_string)
|
|
90
|
+
java.util.logging.Logger.getLogger(package_string)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# number of spaces that separate the property from the value in the create_string method
|
|
94
|
+
TO_S_SIZE = 14
|
|
95
|
+
|
|
96
|
+
def create_string(element)
|
|
97
|
+
ret = []
|
|
98
|
+
|
|
99
|
+
unless (tag = element.getTagName).empty?
|
|
100
|
+
ret << "tag:".ljust(TO_S_SIZE) + tag
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
element.getAttributes.each do |_, attribute|
|
|
104
|
+
ret << " #{attribute.getName}:".ljust(TO_S_SIZE + 2) + attribute.getValue.to_s
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
unless (text = element.asText).empty?
|
|
108
|
+
ret << " text:".ljust(TO_S_SIZE + 2) + element.asText
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
ret.join("\n")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
#
|
|
115
|
+
# Used internally.
|
|
116
|
+
#
|
|
117
|
+
# @param [String] string The string to match against.
|
|
118
|
+
# @param [Regexp, String, #to_s] what The match we're looking for.
|
|
119
|
+
# @return [Fixnum, true, false, nil]
|
|
120
|
+
#
|
|
121
|
+
# @api private
|
|
122
|
+
#
|
|
123
|
+
|
|
124
|
+
def matches?(string, what)
|
|
125
|
+
Regexp === what ? string.strip =~ what : string == what.to_s
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Celerity
|
|
2
|
+
class ViewerConnection
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# Create a new connection to the given host/port
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
def self.create(host, port)
|
|
9
|
+
# if the connection fails, we won't spend time loading json
|
|
10
|
+
socket = TCPSocket.new(host, port)
|
|
11
|
+
require "json"
|
|
12
|
+
new(socket)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(socket)
|
|
16
|
+
@socket = socket
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
# Tells the viewer to render the given HTML, with the given URL as base url.
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
def render_html(html, url)
|
|
24
|
+
send_data('method' => 'page_changed', 'html' => html, 'url' => url)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Tells the viewer to save a screenshot of the current page to the given path.
|
|
29
|
+
# May not be available on all viewers.
|
|
30
|
+
#
|
|
31
|
+
|
|
32
|
+
def save(path)
|
|
33
|
+
send_data('method' => 'save', 'path' => path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Tells the viewer to dump the render tree to the given path.
|
|
39
|
+
# Only available in the Qt viewer.
|
|
40
|
+
#
|
|
41
|
+
|
|
42
|
+
def save_render_tree(path)
|
|
43
|
+
send_data('method' => 'save_render_tree', 'path' => path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# Get the currently rendered page as a Base64-encoded PNG image.
|
|
48
|
+
# Only available in the Qt viewer.
|
|
49
|
+
#
|
|
50
|
+
|
|
51
|
+
def image_data
|
|
52
|
+
send_data('method' => 'image_data')
|
|
53
|
+
data = read_data
|
|
54
|
+
data['image'] || data['error']
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
# Close the connection.
|
|
59
|
+
#
|
|
60
|
+
|
|
61
|
+
def close
|
|
62
|
+
@socket.close rescue nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def send_data(msg)
|
|
68
|
+
json = msg.to_json
|
|
69
|
+
data = "Content-Length: #{json.size}\n\n#{json}"
|
|
70
|
+
@socket.write data
|
|
71
|
+
@socket.flush
|
|
72
|
+
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def read_data
|
|
77
|
+
buf = ''
|
|
78
|
+
until buf =~ /\n\n\z/ || @socket.eof? || @socket.closed?
|
|
79
|
+
buf << @socket.read(1).to_s
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
return if buf.empty?
|
|
83
|
+
|
|
84
|
+
length = buf[/Content-Length: (\d+)/, 1].to_i
|
|
85
|
+
JSON.parse @socket.read(length)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Celerity
|
|
2
|
+
class Browser
|
|
3
|
+
class << self
|
|
4
|
+
|
|
5
|
+
# Added for Watir compatibility - not in use by Celerity
|
|
6
|
+
attr_accessor :speed, :attach_timeout, :visible
|
|
7
|
+
# Added for Watir compatibility - not in use by Celerity
|
|
8
|
+
alias_method :start_window, :start
|
|
9
|
+
# Added for Watir compatibility - not in use by Celerity
|
|
10
|
+
def each; end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Added for Watir compatibility - not in use by Celerity
|
|
14
|
+
attr_accessor :visible
|
|
15
|
+
|
|
16
|
+
# Added for Watir compatibility - not in use by Celerity
|
|
17
|
+
def bring_to_front; true; end
|
|
18
|
+
# Added for Watir compatibility - not in use by Celerity
|
|
19
|
+
def speed=(s); s end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
module ClickableElement
|
|
24
|
+
alias_method :click_no_wait, :click
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module Container
|
|
28
|
+
alias_method :checkbox, :check_box
|
|
29
|
+
alias_method :checkBox, :check_box
|
|
30
|
+
alias_method :body, :tbody
|
|
31
|
+
alias_method :bodies, :tbodies
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Element
|
|
35
|
+
alias_method :exists, :exists?
|
|
36
|
+
alias_method :innerText, :text
|
|
37
|
+
alias_method :inner_text, :text
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Image
|
|
41
|
+
alias_method :hasLoaded?, :loaded?
|
|
42
|
+
alias_method :fileSize, :file_size
|
|
43
|
+
alias_method :fileCreatedDate, :file_created_date
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Link
|
|
47
|
+
alias_method :click_no_wait, :click
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class RadioCheckCommon
|
|
51
|
+
alias_method :isSet?, :set?
|
|
52
|
+
alias_method :getState, :set?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class SelectList
|
|
56
|
+
alias_method :getSelectedItems, :selected_options
|
|
57
|
+
alias_method :getAllContents, :options
|
|
58
|
+
alias_method :clearSelection, :clear
|
|
59
|
+
alias_method :includes?, :include?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class TextField
|
|
63
|
+
alias_method :dragContentsTo, :drag_contents_to
|
|
64
|
+
alias_method :getContents, :value
|
|
65
|
+
|
|
66
|
+
def requires_typing; end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Celerity::IE = Celerity::Browser
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Celerity
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Module to search by xpath
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
module XpathSupport
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Get the first element found matching the given XPath.
|
|
11
|
+
#
|
|
12
|
+
# @param [String] xpath
|
|
13
|
+
# @return [Celerity::Element] An element subclass (or Element if none is found)
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
def element_by_xpath(xpath)
|
|
17
|
+
assert_exists
|
|
18
|
+
obj = @page.getFirstByXPath(xpath)
|
|
19
|
+
element_from_dom_node(obj, ":xpath and #{xpath.inspect}")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# Get all the elements matching the given XPath.
|
|
24
|
+
#
|
|
25
|
+
# @param [String] xpath
|
|
26
|
+
# @return [Array<Celerity::Element>] array of elements
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
def elements_by_xpath(xpath)
|
|
30
|
+
assert_exists
|
|
31
|
+
objects = @page.getByXPath(xpath)
|
|
32
|
+
# should use an ElementCollection here?
|
|
33
|
+
objects.map { |o| element_from_dom_node(o) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Convert the given HtmlUnit DomNode to a Celerity object
|
|
38
|
+
#
|
|
39
|
+
|
|
40
|
+
def element_from_dom_node(obj, identifier_string = nil)
|
|
41
|
+
element_class = Util.htmlunit2celerity(obj.class) || Element
|
|
42
|
+
element = element_class.new(self, :object, obj)
|
|
43
|
+
|
|
44
|
+
element.identifier_string = identifier_string
|
|
45
|
+
element.extend(ClickableElement) unless element.is_a?(ClickableElement)
|
|
46
|
+
|
|
47
|
+
element
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require File.expand_path("../watirspec/spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "Browser" do
|
|
4
|
+
|
|
5
|
+
describe "#credentials=" do
|
|
6
|
+
it "sets the basic authentication credentials" do
|
|
7
|
+
browser.goto(WatirSpec.host + "/authentication")
|
|
8
|
+
|
|
9
|
+
browser.text.should_not include("ok")
|
|
10
|
+
browser.credentials = "foo:bar"
|
|
11
|
+
|
|
12
|
+
browser.goto(WatirSpec.host + "/authentication")
|
|
13
|
+
browser.text.should include("ok")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require File.expand_path("../watirspec/spec_helper", __FILE__)
|
|
4
|
+
|
|
5
|
+
describe "Browser" do
|
|
6
|
+
describe "#new" do
|
|
7
|
+
it "raises TypeError if argument is not a Hash" do
|
|
8
|
+
lambda { Browser.new(:foo) }.should raise_error(TypeError)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "raises ArgumentError if given bad arguments for :render key" do
|
|
12
|
+
lambda { Browser.new(:render => :foo) }.should raise_error(ArgumentError)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "raises ArgumentError if given bad arguments for :browser key" do
|
|
16
|
+
lambda { Browser.new(:browser => 'foo') }.should raise_error(ArgumentError)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "raises ArgumentError if given an unknown option" do
|
|
20
|
+
lambda { Browser.new(:foo => 1) }.should raise_error(ArgumentError)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should hold the init options" do
|
|
24
|
+
browser.options.should == WatirSpec.implementation.browser_args.first
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should use the specified proxy" do
|
|
28
|
+
# TODO: find a better way to test this with rack
|
|
29
|
+
require 'webrick/httpproxy'
|
|
30
|
+
|
|
31
|
+
received = false
|
|
32
|
+
blk = lambda { received = true }
|
|
33
|
+
port = WatirSpec::Server.find_free_port_above(2001)
|
|
34
|
+
|
|
35
|
+
s = WEBrick::HTTPProxyServer.new(:Port => port,
|
|
36
|
+
:ProxyContentHandler => blk)
|
|
37
|
+
Thread.new { s.start }
|
|
38
|
+
|
|
39
|
+
opts = WatirSpec.implementation.browser_args.first.merge(:proxy => "localhost:#{port}")
|
|
40
|
+
|
|
41
|
+
begin
|
|
42
|
+
b = Browser.new(opts)
|
|
43
|
+
b.goto(WatirSpec.host)
|
|
44
|
+
ensure
|
|
45
|
+
s.shutdown
|
|
46
|
+
b.close if b
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
received.should be_true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should use the specified user agent" do
|
|
53
|
+
opts = WatirSpec.implementation.browser_args.first.merge(:user_agent => "Celerity")
|
|
54
|
+
|
|
55
|
+
b = Browser.new(opts)
|
|
56
|
+
|
|
57
|
+
begin
|
|
58
|
+
b.goto(WatirSpec.host + "/header_echo")
|
|
59
|
+
b.text.should include('"HTTP_USER_AGENT"=>"Celerity"')
|
|
60
|
+
ensure
|
|
61
|
+
b.close
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "does not try to find a viewer if created with :viewer => false" do
|
|
66
|
+
ViewerConnection.should_not_receive(:create)
|
|
67
|
+
|
|
68
|
+
Browser.new(:viewer => false).close
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "tries to find a viewer if created with :viewer => nil" do
|
|
72
|
+
ViewerConnection.should_receive(:create).with("127.0.0.1", 6429)
|
|
73
|
+
|
|
74
|
+
Browser.new(:viewer => nil).close
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "tries to find a viewer on the specified host/port with :viewer => String" do
|
|
78
|
+
ViewerConnection.should_receive(:create).with("localhost", 1234)
|
|
79
|
+
|
|
80
|
+
Browser.new(:viewer => "localhost:1234").close
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should use the specified cache limit" do
|
|
84
|
+
opts = WatirSpec.implementation.browser_args.first.merge(:cache_limit => 100)
|
|
85
|
+
b = Browser.new(opts)
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
b.cache_limit.should == 100
|
|
89
|
+
ensure
|
|
90
|
+
b.close
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should use the Firefox 3 browser version when specified" do
|
|
95
|
+
Browser.new(:browser => :firefox).webclient.browser_version.nickname.should == "FF3"
|
|
96
|
+
Browser.new(:browser => :firefox3).webclient.browser_version.nickname.should == "FF3"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should use the Firefox 3.6 browser version when specified" do
|
|
100
|
+
Browser.new(:browser => :firefox_3_6).webclient.browser_version.nickname.should == "FF3.6"
|
|
101
|
+
Browser.new(:browser => :ff36).webclient.browser_version.nickname.should == "FF3.6"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should use the Internet Explorer 7 browser version when specified" do
|
|
105
|
+
Browser.new(:browser => :internet_explorer).webclient.browser_version.nickname.should == "IE7"
|
|
106
|
+
Browser.new(:browser => :internet_explorer7).webclient.browser_version.nickname.should == "IE7"
|
|
107
|
+
Browser.new(:browser => :internet_explorer_7).webclient.browser_version.nickname.should == "IE7"
|
|
108
|
+
Browser.new(:browser => :ie).webclient.browser_version.nickname.should == "IE7"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should use the Internet Explorer 8 browser version when specified" do
|
|
112
|
+
Browser.new(:browser => :internet_explorer_8).webclient.browser_version.nickname.should == "IE8"
|
|
113
|
+
Browser.new(:browser => :ie8).webclient.browser_version.nickname.should == "IE8"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "should turn off CSS" do
|
|
117
|
+
b = Browser.new(:css => false)
|
|
118
|
+
b.css.should be_false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe "#html" do
|
|
124
|
+
%w(shift_jis iso-2022-jp euc-jp).each do |charset|
|
|
125
|
+
it "returns decoded #{charset.upcase} when :charset specified" do
|
|
126
|
+
opts = WatirSpec.implementation.browser_args.first.merge(:charset => charset.upcase)
|
|
127
|
+
browser = Browser.new(opts)
|
|
128
|
+
|
|
129
|
+
begin
|
|
130
|
+
browser.goto(WatirSpec.files + "/#{charset}_text.html")
|
|
131
|
+
# Browser#text is automagically transcoded into the right charset, but Browser#html isn't.
|
|
132
|
+
browser.html.should =~ /本日は晴天なり。/
|
|
133
|
+
ensure
|
|
134
|
+
browser.close
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "does not fail for huge pages" do
|
|
140
|
+
browser.goto WatirSpec.host + "/big"
|
|
141
|
+
browser.html.should include("hello</body>")
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
describe "#text" do
|
|
146
|
+
it "does not fail for huge pages" do
|
|
147
|
+
browser.goto WatirSpec.host + "/big"
|
|
148
|
+
browser.text.should include("hello")
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
describe "#response_headers" do
|
|
153
|
+
it "returns the response headers (as a hash)" do
|
|
154
|
+
browser.goto(WatirSpec.host + "/non_control_elements.html")
|
|
155
|
+
browser.response_headers.should be_kind_of(Hash)
|
|
156
|
+
browser.response_headers['Date'].should be_kind_of(String)
|
|
157
|
+
browser.response_headers['Content-Type'].should be_kind_of(String)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
describe "#content_type" do
|
|
162
|
+
it "returns the content type" do
|
|
163
|
+
browser.goto(WatirSpec.host + "/non_control_elements.html")
|
|
164
|
+
browser.content_type.should =~ /\w+\/\w+/
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe "#io" do
|
|
169
|
+
it "returns the io object of the content" do
|
|
170
|
+
browser.goto(WatirSpec.files + "/non_control_elements.html")
|
|
171
|
+
browser.io.should be_kind_of(IO)
|
|
172
|
+
browser.io.read.should == File.read("#{WatirSpec.html}/non_control_elements.html")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe "#goto" do
|
|
177
|
+
it "raises UnexpectedPageException if the content type is not understood" do
|
|
178
|
+
lambda { browser.goto(WatirSpec.host + "/octet_stream") }.should raise_error(UnexpectedPageException)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "takes a 2nd argument of headers" do
|
|
182
|
+
browser.goto(WatirSpec.host + "/header_echo", {'Accept-Language'=>'fr','Accept'=>'application/json'})
|
|
183
|
+
browser.text.should include('"HTTP_ACCEPT"=>"application/json"')
|
|
184
|
+
browser.text.should include('"HTTP_ACCEPT_LANGUAGE"=>"fr"')
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
describe "#cookies" do
|
|
189
|
+
it "returns set cookies as a Ruby hash" do
|
|
190
|
+
browser = WatirSpec.new_browser
|
|
191
|
+
begin
|
|
192
|
+
browser.cookies.should == {}
|
|
193
|
+
|
|
194
|
+
browser.goto(WatirSpec.host + "/set_cookie")
|
|
195
|
+
|
|
196
|
+
cookies = browser.cookies
|
|
197
|
+
cookies.size.should == 1
|
|
198
|
+
cookies[WatirSpec::Server.bind]['monster'].should == "/"
|
|
199
|
+
ensure
|
|
200
|
+
browser.close
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
describe "#clear_cookies" do
|
|
206
|
+
it "clears all cookies" do
|
|
207
|
+
b = WatirSpec.new_browser
|
|
208
|
+
|
|
209
|
+
begin
|
|
210
|
+
b.cookies.should be_empty
|
|
211
|
+
|
|
212
|
+
b.goto(WatirSpec.host + "/set_cookie")
|
|
213
|
+
b.cookies.size.should == 1
|
|
214
|
+
b.clear_cookies
|
|
215
|
+
b.cookies.should be_empty
|
|
216
|
+
ensure
|
|
217
|
+
b.close
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
describe "add_cookie" do
|
|
223
|
+
it "adds a cookie with the given domain, name and value" do
|
|
224
|
+
begin
|
|
225
|
+
browser.add_cookie("example.com", "foo", "bar")
|
|
226
|
+
cookies = browser.cookies
|
|
227
|
+
cookies.should be_instance_of(Hash)
|
|
228
|
+
cookies.should have_key('example.com')
|
|
229
|
+
cookies['example.com']['foo'].should == 'bar'
|
|
230
|
+
ensure
|
|
231
|
+
browser.clear_cookies
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "adds a cookie with the specified options" do
|
|
236
|
+
begin
|
|
237
|
+
browser.add_cookie("example.com", "foo", "bar", :path => "/foobar", :expires => Time.now + 100000)
|
|
238
|
+
cookies = browser.cookies
|
|
239
|
+
cookies.should be_instance_of(Hash)
|
|
240
|
+
cookies['example.com']['foo'].should == 'bar'
|
|
241
|
+
ensure
|
|
242
|
+
browser.clear_cookies
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe "remove_cookie" do
|
|
248
|
+
it "removes the cookie for the given domain and name" do
|
|
249
|
+
b = WatirSpec.new_browser
|
|
250
|
+
begin
|
|
251
|
+
b.goto(WatirSpec.host + "/set_cookie")
|
|
252
|
+
|
|
253
|
+
b.remove_cookie(WatirSpec::Server.bind, "monster")
|
|
254
|
+
b.cookies.should be_empty
|
|
255
|
+
ensure
|
|
256
|
+
b.close
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it "raises an error if no such cookie exists" do
|
|
261
|
+
lambda { browser.remove_cookie("bogus.com", "bar") }.should raise_error(CookieNotFoundError)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
describe "#wait" do
|
|
266
|
+
it "should wait for javascript timers to finish" do
|
|
267
|
+
alerts = 0
|
|
268
|
+
browser.add_listener(:alert) { alerts += 1 }
|
|
269
|
+
browser.goto(WatirSpec.files + "/timeout.html")
|
|
270
|
+
browser.div(:id, 'alert').click
|
|
271
|
+
browser.wait.should be_true
|
|
272
|
+
alerts.should == 1
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
it "should pass the correct args to webclient" do
|
|
276
|
+
browser.webclient.should_receive(:waitForBackgroundJavaScript).with(10000)
|
|
277
|
+
browser.wait
|
|
278
|
+
|
|
279
|
+
browser.webclient.should_receive(:waitForBackgroundJavaScript).with(3000)
|
|
280
|
+
browser.wait(3)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
describe "#wait_while" do
|
|
286
|
+
it "waits until the specified condition becomes false" do
|
|
287
|
+
browser.goto(WatirSpec.files + "/timeout.html")
|
|
288
|
+
browser.div(:id, "change").click
|
|
289
|
+
browser.wait_while { browser.contains_text("Trigger change") }
|
|
290
|
+
browser.wait_until {
|
|
291
|
+
browser.div(:id, "change").text == "all done"
|
|
292
|
+
}
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "returns the value returned from the block" do
|
|
296
|
+
browser.wait_while { false }.should == false
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
describe "#wait_until" do
|
|
301
|
+
it "waits until the condition becomes true" do
|
|
302
|
+
browser.goto(WatirSpec.files + "/timeout.html")
|
|
303
|
+
browser.div(:id, "change").click
|
|
304
|
+
browser.wait_until { browser.contains_text("all done") }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "returns the value returned from the block" do
|
|
308
|
+
browser.wait_until { true }.should == true
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
describe "#element_by_xpath" do
|
|
313
|
+
it "returns usable elements even though they're not supported" do
|
|
314
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
315
|
+
|
|
316
|
+
el = browser.element_by_xpath("//link")
|
|
317
|
+
el.should be_instance_of(Celerity::Element)
|
|
318
|
+
el.should be_kind_of(Celerity::ClickableElement)
|
|
319
|
+
el.rel.should == "stylesheet"
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it "includes the xpath in an exception message" do
|
|
323
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
324
|
+
|
|
325
|
+
the_xpath = "//div[contains(@class, 'this does not exist')]"
|
|
326
|
+
el = browser.element_by_xpath(the_xpath)
|
|
327
|
+
|
|
328
|
+
lambda { el.visible? }.should raise_error(
|
|
329
|
+
Celerity::Exception::UnknownObjectException, Regexp.new(Regexp.escape(the_xpath)))
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
describe "#focused_element" do
|
|
334
|
+
it "returns the element that currently has the focus" do
|
|
335
|
+
b = WatirSpec.new_browser
|
|
336
|
+
b.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
337
|
+
b.focused_element.id.should == "new_user_first_name"
|
|
338
|
+
|
|
339
|
+
b.close
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
describe "#status_code" do
|
|
344
|
+
it "returns the status code of the last request" do
|
|
345
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
346
|
+
browser.status_code.should == 200
|
|
347
|
+
|
|
348
|
+
browser.goto(WatirSpec.host + "/doesnt_exist")
|
|
349
|
+
browser.status_code.should == 404
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
describe "#status_code_exceptions" do
|
|
354
|
+
it "raises status code exceptions if set to true" do
|
|
355
|
+
browser.status_code_exceptions = true
|
|
356
|
+
|
|
357
|
+
begin
|
|
358
|
+
lambda {
|
|
359
|
+
browser.goto(WatirSpec.host + "/doesnt_exist")
|
|
360
|
+
}.should raise_error(NavigationException)
|
|
361
|
+
ensure
|
|
362
|
+
browser.status_code_exceptions = false
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
describe "#javascript_exceptions" do
|
|
368
|
+
it "raises javascript exceptions if set to true" do
|
|
369
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
370
|
+
browser.javascript_exceptions = true
|
|
371
|
+
begin
|
|
372
|
+
lambda { browser.execute_script("no_such_function()") }.should raise_error
|
|
373
|
+
ensure
|
|
374
|
+
browser.javascript_exceptions = false
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
describe "#add_listener" do
|
|
380
|
+
it "should click OK for confirm() calls" do
|
|
381
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
382
|
+
browser.add_listener(:confirm) { }
|
|
383
|
+
browser.execute_script("confirm()").should == true
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
describe "#remove_listener" do
|
|
388
|
+
it "should remove the given listener Proc" do
|
|
389
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
390
|
+
|
|
391
|
+
called = false
|
|
392
|
+
listener = lambda { called = true }
|
|
393
|
+
|
|
394
|
+
browser.add_listener(:alert, &listener)
|
|
395
|
+
browser.execute_script("alert('foo')")
|
|
396
|
+
called.should be_true
|
|
397
|
+
|
|
398
|
+
called = false
|
|
399
|
+
browser.remove_listener(:alert, listener)
|
|
400
|
+
browser.execute_script("alert('foo')")
|
|
401
|
+
called.should be_false
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
describe "#add_checker" do
|
|
406
|
+
|
|
407
|
+
# watir only supports a lambda instance as argument, celerity supports both
|
|
408
|
+
it "runs the given block on each page load" do
|
|
409
|
+
output = ''
|
|
410
|
+
|
|
411
|
+
browser.add_checker { |browser| output << browser.text }
|
|
412
|
+
browser.goto(WatirSpec.files + "/non_control_elements.html")
|
|
413
|
+
|
|
414
|
+
output.should include('Dubito, ergo cogito, ergo sum')
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
describe "#confirm" do
|
|
420
|
+
it "clicks 'OK' for a confirm() call" do
|
|
421
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
422
|
+
|
|
423
|
+
browser.confirm(true) do
|
|
424
|
+
browser.execute_script('confirm()').should be_true
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
it "clicks 'cancel' for a confirm() call" do
|
|
429
|
+
browser.goto(WatirSpec.files + "/forms_with_input_elements.html")
|
|
430
|
+
|
|
431
|
+
browser.confirm(false) do
|
|
432
|
+
browser.execute_script('confirm()').should be_false
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
end
|