selenium-client 1.1
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/README +7 -0
- data/lib/selenium.rb +36 -0
- data/lib/selenium/client/base.rb +52 -0
- data/lib/selenium/client/driver.rb +9 -0
- data/lib/selenium/client/generated_driver.rb +1621 -0
- data/lib/selenium/client/selenese_client.rb +95 -0
- data/lib/selenium/client/selenium_helper.rb +36 -0
- data/lib/selenium/command_error.rb +4 -0
- data/lib/selenium/protocol_error.rb +4 -0
- data/lib/selenium/rspec/screenshot_formatter.rb +187 -0
- data/lib/selenium/screenshot_saver.rb +25 -0
- data/test/all_unit_tests.rb +3 -0
- metadata +67 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
module Selenium
|
2
|
+
module Client
|
3
|
+
|
4
|
+
HTTP_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8' }
|
5
|
+
|
6
|
+
module SeleneseClient
|
7
|
+
attr_reader :session_id
|
8
|
+
|
9
|
+
def do_command(verb, args)
|
10
|
+
timeout(@timeout) do
|
11
|
+
status, response = http_post(http_request_for(verb, args))
|
12
|
+
raise SeleniumCommandError, response unless status == "OK"
|
13
|
+
response
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_string(verb, args)
|
18
|
+
do_command(verb, args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_string_array(verb, args)
|
22
|
+
csv = get_string(verb, args)
|
23
|
+
token = ""
|
24
|
+
tokens = []
|
25
|
+
escape = false
|
26
|
+
csv.split(//).each do |letter|
|
27
|
+
if escape
|
28
|
+
token += letter
|
29
|
+
escape = false
|
30
|
+
next
|
31
|
+
end
|
32
|
+
case letter
|
33
|
+
when '\\'
|
34
|
+
escape = true
|
35
|
+
when ','
|
36
|
+
tokens << token
|
37
|
+
token = ""
|
38
|
+
else
|
39
|
+
token += letter
|
40
|
+
end
|
41
|
+
end
|
42
|
+
tokens << token
|
43
|
+
return tokens
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_number(verb, args)
|
47
|
+
# Is there something I need to do here?
|
48
|
+
return get_string(verb, args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_number_array(verb, args)
|
52
|
+
# Is there something I need to do here?
|
53
|
+
return get_string_array(verb, args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_boolean(verb, args)
|
57
|
+
parse_boolean_value get_string(verb, args)
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_boolean_array(verb, args)
|
61
|
+
get_string_array(verb, args).collect {|value| parse_boolean_value(value)}
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def parse_boolean_value(value)
|
67
|
+
if ("true" == value)
|
68
|
+
return true
|
69
|
+
elsif ("false" == value)
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
raise ProtocolError, "Invalid Selenese boolean value that is neither 'true' nor 'false': got '#{value}'"
|
73
|
+
end
|
74
|
+
|
75
|
+
def http_request_for(verb, args)
|
76
|
+
data = "cmd=#{CGI::escape(verb)}"
|
77
|
+
args.each_with_index do |arg, index|
|
78
|
+
data << "&#{index.succ}=#{CGI::escape(arg.to_s)}"
|
79
|
+
end
|
80
|
+
data << "&sessionId=#{session_id}" unless session_id.nil?
|
81
|
+
data
|
82
|
+
end
|
83
|
+
|
84
|
+
def http_post(data)
|
85
|
+
#print "Requesting --->" + command_string + "\n"
|
86
|
+
http = Net::HTTP.new(@server_host, @server_port)
|
87
|
+
response = http.post('/selenium-server/driver/', data, HTTP_HEADERS)
|
88
|
+
#print "RESULT: " + response.body + "\n\n"å
|
89
|
+
[ response.body[0..1], response.body[3..-1] ]
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Defines a mixin module that you can use to write Selenium tests
|
2
|
+
# without typing "@selenium." in front of every command. Every
|
3
|
+
# call to a missing method will be automatically sent to the @selenium
|
4
|
+
# object.
|
5
|
+
module Selenium
|
6
|
+
module Client
|
7
|
+
|
8
|
+
module SeleniumHelper
|
9
|
+
|
10
|
+
# Overrides standard "open" method with @selenium.open
|
11
|
+
def open(addr)
|
12
|
+
@selenium.open(addr)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Overrides standard "type" method with @selenium.type
|
16
|
+
def type(inputLocator, value)
|
17
|
+
@selenium.type(inputLocator, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Overrides standard "select" method with @selenium.select
|
21
|
+
def select(inputLocator, optionLocator)
|
22
|
+
@selenium.select(inputLocator, optionLocator)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Passes all calls to missing methods to @selenium
|
26
|
+
def method_missing(method_name, *args)
|
27
|
+
if args.empty?
|
28
|
+
@selenium.send(method_name)
|
29
|
+
else
|
30
|
+
@selenium.send(method_name, *args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require "digest/md5"
|
2
|
+
require "base64"
|
3
|
+
require "rubygems"
|
4
|
+
require "spec"
|
5
|
+
require 'spec/runner/formatter/html_formatter'
|
6
|
+
|
7
|
+
module Selenium
|
8
|
+
module RSpec
|
9
|
+
class ScreenshotFormatter < Spec::Runner::Formatter::HtmlFormatter
|
10
|
+
PLACEHOLDER = "<<placeholder>>"
|
11
|
+
|
12
|
+
################### Hooks? ########
|
13
|
+
|
14
|
+
def start(example_count)
|
15
|
+
super
|
16
|
+
# ensure there's at least 1 example group header (normally 0 with deep_test)
|
17
|
+
# prevents js and html validity errors
|
18
|
+
example_group = Object.new
|
19
|
+
def example_group.description; ""; end
|
20
|
+
add_example_group(example_group)
|
21
|
+
end
|
22
|
+
|
23
|
+
def move_progress
|
24
|
+
# we don't have current_example_number, and we don't really care about the progress bar
|
25
|
+
end
|
26
|
+
|
27
|
+
def extra_failure_content(failure)
|
28
|
+
super + PLACEHOLDER
|
29
|
+
end
|
30
|
+
|
31
|
+
def example_passed(example)
|
32
|
+
include_example_group_description example
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def example_pending(example_group_description, example, message)
|
37
|
+
include_example_group_description example
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def example_failed(example, counter, failure)
|
42
|
+
include_example_group_description example
|
43
|
+
old_output = @output
|
44
|
+
@output = StringIO.new
|
45
|
+
super
|
46
|
+
result = @output.string
|
47
|
+
result.gsub! PLACEHOLDER, html_capture(example)
|
48
|
+
old_output.puts result
|
49
|
+
old_output.flush
|
50
|
+
ensure
|
51
|
+
@output = old_output
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
###### Called from After each ####
|
56
|
+
|
57
|
+
def self.capture_browser_state(selenium_driver, example)
|
58
|
+
# Selenium RC seems to 'freeze' every so often when calling getHTMLSource, especially when DeepTest timeout is low, I need to investigate...
|
59
|
+
# Set deeptest :timeout_in_seconds => 30 to see it happen
|
60
|
+
capture_html_snapshot selenium_driver, example
|
61
|
+
capture_screenshot selenium_driver, example
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.capture_html_snapshot(selenium_driver, example)
|
65
|
+
html = selenium_driver.get_html_source
|
66
|
+
File.open(file_for_html_capture(example), "w") { |f| f.write html }
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.capture_screenshot(selenium_driver, example)
|
70
|
+
selenium_driver.window_maximize
|
71
|
+
encodedImage = selenium_driver.capture_screenshot_to_string
|
72
|
+
pngImage = Base64.decode64(encodedImage)
|
73
|
+
File.open(file_for_screenshot_capture(example), "w") { |f| f.write pngImage }
|
74
|
+
end
|
75
|
+
|
76
|
+
################### Instrumentation ########
|
77
|
+
|
78
|
+
def html_capture(example)
|
79
|
+
dom_id = "example_" + self.class.hash_for_example(example)
|
80
|
+
screenshot_url = self.class.relative_file_for_png_capture(example)
|
81
|
+
snapshot_url = self.class.relative_file_for_html_capture(example)
|
82
|
+
<<-EOS
|
83
|
+
<div>[<a id="#{dom_id}_screenshot_link" href="javascript:toggleVisilibility('#{dom_id}_screenshot', 'Screenshot');">Show screenshot</a>]</div>
|
84
|
+
<br/>
|
85
|
+
<div id="#{dom_id}_screenshot" style="display: none">
|
86
|
+
<a href="#{screenshot_url}">
|
87
|
+
<img width="80%" src="#{screenshot_url}" />
|
88
|
+
</a>
|
89
|
+
</div>
|
90
|
+
<br/>
|
91
|
+
|
92
|
+
<div>[<a id="#{dom_id}_snapshot_link" href=\"javascript:toggleVisilibility('#{dom_id}_snapshot', 'Snapshot')\">Show snapshot</a>]</div>
|
93
|
+
<br/><br/>
|
94
|
+
<div id="#{dom_id}_snapshot" class="dyn-source">
|
95
|
+
<a href="#{snapshot_url}">Full screen</a><br/><br/>
|
96
|
+
<iframe src="#{snapshot_url}" width="100%" height="600px" ></iframe>
|
97
|
+
</div>
|
98
|
+
EOS
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def self.relative_file_for_html_capture(example)
|
103
|
+
"resources/example_#{hash_for_example(example)}.html"
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.relative_file_for_png_capture(example)
|
107
|
+
"resources/example_#{hash_for_example(example)}.png"
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.file_for_html_capture(example)
|
111
|
+
file_name = capture_root_dir + "/example_#{hash_for_example(example)}.html"
|
112
|
+
FileUtils.mkdir_p(capture_root_dir) unless File.directory?(capture_root_dir)
|
113
|
+
file_name
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.file_for_screenshot_capture(example)
|
117
|
+
file_name = capture_root_dir + "/example_#{hash_for_example(example)}.png"
|
118
|
+
FileUtils.mkdir_p(capture_root_dir) unless File.directory?(capture_root_dir)
|
119
|
+
file_name
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.hash_for_example(example)
|
123
|
+
Digest::MD5.hexdigest example.implementation_backtrace.first
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def self.capture_root_dir
|
128
|
+
root_dir + "/resources"
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.root_dir
|
132
|
+
(ENV['CC_BUILD_ARTIFACTS'] || './tmp/rspec_report')
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def include_example_group_description(example)
|
137
|
+
def example.description
|
138
|
+
self.class.description.to_s + " :: " + super
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def report_header
|
144
|
+
super + "\n<script type=\"text/javascript\">moveProgressBar('100.0');</script>"
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
def global_scripts
|
150
|
+
super + <<-EOF
|
151
|
+
function toggleVisilibility(id, description) {
|
152
|
+
var section;
|
153
|
+
var link;
|
154
|
+
|
155
|
+
section = document.getElementById(id);
|
156
|
+
link = document.getElementById(id + "_link");
|
157
|
+
|
158
|
+
if (section.style.display == "block") {
|
159
|
+
section.style.display = "none"
|
160
|
+
link.innerHTML = description
|
161
|
+
} else {
|
162
|
+
section.style.display = "block"
|
163
|
+
link.innerHTML = "Hide " + description
|
164
|
+
}
|
165
|
+
}
|
166
|
+
EOF
|
167
|
+
end
|
168
|
+
|
169
|
+
def global_styles
|
170
|
+
super + <<-EOF
|
171
|
+
div.rspec-report textarea {
|
172
|
+
width: 100%;
|
173
|
+
}
|
174
|
+
|
175
|
+
div.rspec-report .dyn-source {
|
176
|
+
background: #FFFFEE none repeat scroll 0%;
|
177
|
+
border:1px dotted black;
|
178
|
+
color: #000000;
|
179
|
+
display: none;
|
180
|
+
margin: 0.5em 2em;
|
181
|
+
padding: 0.5em;
|
182
|
+
}
|
183
|
+
EOF
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Selenium
|
2
|
+
module ScreenshotSaver
|
3
|
+
|
4
|
+
def save_screenshot_to(png_path)
|
5
|
+
dir = File.dirname(png_path)
|
6
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
7
|
+
if PLATFORM['darwin']
|
8
|
+
sh "screencapture '#{png_path}'"
|
9
|
+
elsif image_magick_support?
|
10
|
+
sh "import -window root '#{png_path}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def image_magick_support?
|
15
|
+
@image_magick_support ||= `import --version`.grep /"ImageMagick"/
|
16
|
+
end
|
17
|
+
|
18
|
+
def sh(command)
|
19
|
+
system command
|
20
|
+
STDERR.puts "Warning: Could not capture screenshot with '#{command}'" unless $? == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: selenium-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- OpenQA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: selenium-client@rubyforge.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- lib/selenium/client/base.rb
|
26
|
+
- lib/selenium/client/driver.rb
|
27
|
+
- lib/selenium/client/generated_driver.rb
|
28
|
+
- lib/selenium/client/selenese_client.rb
|
29
|
+
- lib/selenium/client/selenium_helper.rb
|
30
|
+
- lib/selenium/command_error.rb
|
31
|
+
- lib/selenium/protocol_error.rb
|
32
|
+
- lib/selenium/rspec/screenshot_formatter.rb
|
33
|
+
- lib/selenium/screenshot_saver.rb
|
34
|
+
- lib/selenium.rb
|
35
|
+
- README
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://selenium-client.rubyforge.com
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options:
|
40
|
+
- --title
|
41
|
+
- Selenium Client
|
42
|
+
- --main
|
43
|
+
- README
|
44
|
+
- --line-numbers
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project: selenium-client
|
62
|
+
rubygems_version: 1.2.0
|
63
|
+
signing_key:
|
64
|
+
specification_version: 2
|
65
|
+
summary: Official Ruby Client for Selenium RC.
|
66
|
+
test_files:
|
67
|
+
- test/all_unit_tests.rb
|