selenium-client 1.1 → 1.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/lib/nautilus/shell.rb +32 -0
- data/lib/selenium.rb +8 -0
- data/lib/selenium/client/base.rb +26 -5
- data/lib/selenium/client/driver.rb +1 -0
- data/lib/selenium/client/idiomatic.rb +179 -0
- data/lib/selenium/client/selenese_client.rb +5 -5
- data/lib/selenium/client/selenium_helper.rb +5 -5
- data/lib/selenium/rake/remote_control_start_task.rb +43 -0
- data/lib/selenium/rake/remote_control_stop_task.rb +28 -0
- data/lib/selenium/rake/tasks.rb +6 -0
- data/lib/selenium/remote_control/remote_control.rb +30 -0
- data/lib/selenium/rspec/reporting/file_path_strategy.rb +70 -0
- data/lib/selenium/rspec/reporting/html_report.rb +123 -0
- data/lib/selenium/rspec/reporting/selenium_test_report_formatter.rb +88 -0
- data/lib/selenium/rspec/reporting/system_capture.rb +72 -0
- data/lib/selenium/rspec/rspec_extensions.rb +43 -0
- data/lib/selenium/rspec/spec_helper.rb +22 -0
- data/lib/tcp_socket_extension.rb +23 -0
- metadata +16 -6
- data/lib/selenium/client/generated_driver.rb +0 -1621
- data/lib/selenium/rspec/screenshot_formatter.rb +0 -187
- data/lib/selenium/screenshot_saver.rb +0 -25
@@ -0,0 +1,70 @@
|
|
1
|
+
module Selenium
|
2
|
+
module RSpec
|
3
|
+
module Reporting
|
4
|
+
|
5
|
+
class FilePathStrategy
|
6
|
+
|
7
|
+
def initialize(final_report_file_path)
|
8
|
+
@final_report_file_path = final_report_file_path
|
9
|
+
@relative_dir = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def base_report_dir
|
13
|
+
@base_report_dir ||= File.dirname(File.expand_path(@final_report_file_path))
|
14
|
+
end
|
15
|
+
|
16
|
+
def relative_dir
|
17
|
+
return @relative_dir if @relative_dir
|
18
|
+
|
19
|
+
file_name_without_extension = File.basename(@final_report_file_path).sub(/\.[^\.]*$/, "")
|
20
|
+
@relative_dir ||= "resources/" + file_name_without_extension
|
21
|
+
end
|
22
|
+
|
23
|
+
def relative_file_path_for_html_capture(example)
|
24
|
+
"#{relative_dir}/example_#{example_hash(example)}.html"
|
25
|
+
end
|
26
|
+
|
27
|
+
def relative_file_path_for_system_screenshot(example)
|
28
|
+
"#{relative_dir}/example_#{example_hash(example)}_system_screenshot.png"
|
29
|
+
end
|
30
|
+
|
31
|
+
def relative_file_path_for_page_screenshot(example)
|
32
|
+
"#{relative_dir}/example_#{example_hash(example)}_page_screenshot.png"
|
33
|
+
end
|
34
|
+
|
35
|
+
def relative_file_path_for_remote_control_logs(example)
|
36
|
+
"#{relative_dir}/example_#{example_hash(example)}_remote_control.log"
|
37
|
+
end
|
38
|
+
|
39
|
+
def file_path_for_html_capture(example)
|
40
|
+
file_path relative_file_path_for_html_capture(example)
|
41
|
+
end
|
42
|
+
|
43
|
+
def file_path_for_system_screenshot(example)
|
44
|
+
file_path relative_file_path_for_system_screenshot(example)
|
45
|
+
end
|
46
|
+
|
47
|
+
def file_path_for_page_screenshot(example)
|
48
|
+
file_path relative_file_path_for_page_screenshot(example)
|
49
|
+
end
|
50
|
+
|
51
|
+
def file_path_for_remote_control_logs(example)
|
52
|
+
file_path relative_file_path_for_remote_control_logs(example)
|
53
|
+
end
|
54
|
+
|
55
|
+
def file_path(relative_file_path)
|
56
|
+
the_file_path = base_report_dir + "/" + relative_file_path
|
57
|
+
parent_dir = File.dirname(the_file_path)
|
58
|
+
FileUtils.mkdir_p(parent_dir) unless File.directory?(parent_dir)
|
59
|
+
the_file_path
|
60
|
+
end
|
61
|
+
|
62
|
+
def example_hash(example)
|
63
|
+
Digest::MD5.hexdigest example.implementation_backtrace.first
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Selenium
|
2
|
+
module RSpec
|
3
|
+
module Reporting
|
4
|
+
|
5
|
+
class HtmlReport
|
6
|
+
|
7
|
+
PLACEHOLDER = "<<placeholder>>"
|
8
|
+
|
9
|
+
def initialize(file_path_strategy)
|
10
|
+
@file_path_strategy = file_path_strategy
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.inject_placeholder(content)
|
14
|
+
content + Selenium::RSpec::Reporting::HtmlReport::PLACEHOLDER
|
15
|
+
end
|
16
|
+
|
17
|
+
def replace_placeholder_with_system_state_content(result, example)
|
18
|
+
result.gsub! PLACEHOLDER, logs_and_screenshot_sections(example)
|
19
|
+
end
|
20
|
+
|
21
|
+
def logs_and_screenshot_sections(example)
|
22
|
+
dom_id = "example_" + @file_path_strategy.example_hash(example)
|
23
|
+
system_screenshot_url = @file_path_strategy.relative_file_path_for_system_screenshot(example)
|
24
|
+
page_screenshot_url = @file_path_strategy.relative_file_path_for_page_screenshot(example)
|
25
|
+
snapshot_url = @file_path_strategy.relative_file_path_for_html_capture(example)
|
26
|
+
remote_control_logs_url = @file_path_strategy.relative_file_path_for_remote_control_logs(example)
|
27
|
+
|
28
|
+
html = ""
|
29
|
+
if File.exists? @file_path_strategy.file_path_for_html_capture(example)
|
30
|
+
html << toggable_section(dom_id, :id => "snapshot", :url=> snapshot_url, :name => "Dynamic HTML Snapshot")
|
31
|
+
end
|
32
|
+
if File.exists? @file_path_strategy.file_path_for_remote_control_logs(example)
|
33
|
+
html << toggable_section(dom_id, :id => "rc_logs", :url=> remote_control_logs_url, :name => "Remote Control Logs")
|
34
|
+
end
|
35
|
+
if File.exists? @file_path_strategy.file_path_for_page_screenshot(example)
|
36
|
+
html << toggable_image_section(dom_id, :id => "page_screenshot", :name => "Page Screenshot", :url => page_screenshot_url)
|
37
|
+
end
|
38
|
+
if File.exists? @file_path_strategy.file_path_for_system_screenshot(example)
|
39
|
+
html << toggable_image_section(dom_id, :id => "system_screenshot", :name => "System Screenshot", :url => system_screenshot_url)
|
40
|
+
end
|
41
|
+
|
42
|
+
return html
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.append_javascript(global_scripts)
|
46
|
+
global_scripts + <<-EOF
|
47
|
+
function toggleVisilibility(id, description) {
|
48
|
+
var section;
|
49
|
+
var link;
|
50
|
+
|
51
|
+
section = document.getElementById(id);
|
52
|
+
link = document.getElementById(id + "_link");
|
53
|
+
|
54
|
+
if (section.style.display == "block") {
|
55
|
+
section.style.display = "none"
|
56
|
+
link.innerHTML = "Show " + description
|
57
|
+
} else {
|
58
|
+
section.style.display = "block"
|
59
|
+
link.innerHTML = "Hide " + description
|
60
|
+
}
|
61
|
+
}
|
62
|
+
EOF
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.append_css(global_styles)
|
66
|
+
global_styles + <<-EOF
|
67
|
+
|
68
|
+
div.rspec-report textarea {
|
69
|
+
width: 100%;
|
70
|
+
}
|
71
|
+
|
72
|
+
div.rspec-report .dyn-source {
|
73
|
+
background: #FFFFEE none repeat scroll 0%;
|
74
|
+
border:1px dotted black;
|
75
|
+
color: #000000;
|
76
|
+
display: none;
|
77
|
+
margin: 0.5em 2em;
|
78
|
+
padding: 0.5em;
|
79
|
+
}
|
80
|
+
|
81
|
+
EOF
|
82
|
+
end
|
83
|
+
|
84
|
+
def toggable_section(dom_id, options)
|
85
|
+
<<-EOS
|
86
|
+
|
87
|
+
<div>[
|
88
|
+
<a id="#{dom_id}_#{options[:id]}_link"
|
89
|
+
href=\"javascript:toggleVisilibility('#{dom_id}_#{options[:id]}', '#{options[:name]}')\">Show #{options[:name]}</a>
|
90
|
+
]</div>
|
91
|
+
<br/><br/>
|
92
|
+
<div id="#{dom_id}_#{options[:id]}" class="dyn-source">
|
93
|
+
<a href="#{options[:url]}">Full screen</a><br/><br/>
|
94
|
+
<iframe src="#{options[:url]}" width="100%" height="600px" ></iframe>
|
95
|
+
</div>
|
96
|
+
|
97
|
+
EOS
|
98
|
+
end
|
99
|
+
|
100
|
+
def toggable_image_section(dom_id, options)
|
101
|
+
<<-EOS
|
102
|
+
|
103
|
+
<div>[<a id="#{dom_id}_#{options[:id]}_link" href="javascript:toggleVisilibility('#{dom_id}_#{options[:id]}', '#{options[:name]}');">Show #{options[:name]}</a>]</div>
|
104
|
+
<br/>
|
105
|
+
<div id="#{dom_id}_#{options[:id]}" style="display: none">
|
106
|
+
<a href="#{options[:url]}">
|
107
|
+
<img width="80%" src="#{options[:url]}" />
|
108
|
+
</a>
|
109
|
+
</div>
|
110
|
+
<br/>
|
111
|
+
|
112
|
+
EOS
|
113
|
+
end
|
114
|
+
|
115
|
+
def report_header
|
116
|
+
super + "\n<script type=\"text/javascript\">moveProgressBar('100.0');</script>"
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# Explicit requires in this file so that we can invoke RSpec runner with a
|
3
|
+
# single:
|
4
|
+
# --require 'lib/selenium/rspec/reporting/selenium_test_report_formatter'
|
5
|
+
#
|
6
|
+
require "digest/md5"
|
7
|
+
require "base64"
|
8
|
+
require "rubygems"
|
9
|
+
require "spec"
|
10
|
+
require 'spec/runner/formatter/html_formatter'
|
11
|
+
require File.expand_path(File.dirname(__FILE__) + "/file_path_strategy")
|
12
|
+
require File.expand_path(File.dirname(__FILE__) + "/system_capture")
|
13
|
+
require File.expand_path(File.dirname(__FILE__) + "/html_report")
|
14
|
+
|
15
|
+
module Selenium
|
16
|
+
module RSpec
|
17
|
+
|
18
|
+
class SeleniumTestReportFormatter < Spec::Runner::Formatter::HtmlFormatter
|
19
|
+
|
20
|
+
def start(example_count)
|
21
|
+
super
|
22
|
+
# ensure there's at least 1 example group header (normally 0 with deep_test)
|
23
|
+
# prevents js and html validity errors
|
24
|
+
example_group = Object.new
|
25
|
+
def example_group.description; ""; end
|
26
|
+
add_example_group(example_group)
|
27
|
+
@@file_path_strategy = Selenium::RSpec::Reporting::FilePathStrategy.new(@where)
|
28
|
+
end
|
29
|
+
|
30
|
+
def move_progress
|
31
|
+
# we don't have current_example_number, and we don't really care about the progress bar
|
32
|
+
end
|
33
|
+
|
34
|
+
def extra_failure_content(failure)
|
35
|
+
Selenium::RSpec::Reporting::HtmlReport.inject_placeholder(super)
|
36
|
+
end
|
37
|
+
|
38
|
+
def example_passed(example)
|
39
|
+
include_example_group_description example
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def example_pending(example, message)
|
44
|
+
include_example_group_description example
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def example_failed(example, counter, failure)
|
49
|
+
include_example_group_description example
|
50
|
+
|
51
|
+
old_output = @output
|
52
|
+
@output = StringIO.new
|
53
|
+
super
|
54
|
+
|
55
|
+
result = @output.string
|
56
|
+
report = Selenium::RSpec::Reporting::HtmlReport.new(@@file_path_strategy)
|
57
|
+
report.replace_placeholder_with_system_state_content(result, example)
|
58
|
+
old_output.puts result
|
59
|
+
old_output.flush
|
60
|
+
ensure
|
61
|
+
@output = old_output
|
62
|
+
end
|
63
|
+
|
64
|
+
# Should be called from config.after(:each) in spec helper
|
65
|
+
def self.capture_system_state(selenium_driver, example)
|
66
|
+
system_capture = Selenium::RSpec::Reporting::SystemCapture.new(selenium_driver, example, @@file_path_strategy)
|
67
|
+
system_capture.capture_system_state
|
68
|
+
end
|
69
|
+
|
70
|
+
def global_scripts
|
71
|
+
Selenium::RSpec::Reporting::HtmlReport.append_javascript(super)
|
72
|
+
end
|
73
|
+
|
74
|
+
def global_styles
|
75
|
+
Selenium::RSpec::Reporting::HtmlReport.append_css(super)
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def include_example_group_description(example)
|
81
|
+
def example.description
|
82
|
+
self.class.description.to_s + " :: " + super
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Selenium
|
2
|
+
module RSpec
|
3
|
+
module Reporting
|
4
|
+
|
5
|
+
class SystemCapture
|
6
|
+
|
7
|
+
def initialize(selenium_driver, example, file_path_strategy)
|
8
|
+
@selenium_driver = selenium_driver
|
9
|
+
@example = example
|
10
|
+
@file_path_strategy = file_path_strategy
|
11
|
+
end
|
12
|
+
|
13
|
+
def capture_system_state
|
14
|
+
# Selenium RC seems to 'freeze' every so often when calling
|
15
|
+
# getHTMLSource, especially when DeepTest timeout is low, I need to investigate...
|
16
|
+
# Set deeptest :timeout_in_seconds => 30 to see it happen
|
17
|
+
begin
|
18
|
+
retrieve_remote_control_logs
|
19
|
+
rescue Exception => e
|
20
|
+
STDERR.puts "WARNING: Could not retrieve remote control logs: #{e}"
|
21
|
+
end
|
22
|
+
begin
|
23
|
+
capture_html_snapshot
|
24
|
+
rescue Exception => e
|
25
|
+
STDERR.puts "WARNING: Could not capture HTML snapshot: #{e}"
|
26
|
+
end
|
27
|
+
begin
|
28
|
+
capture_page_screenshot
|
29
|
+
rescue Exception => e
|
30
|
+
STDERR.puts "WARNING: Could not capture page screenshot: #{e}"
|
31
|
+
end
|
32
|
+
begin
|
33
|
+
capture_system_screenshot
|
34
|
+
rescue Exception => e
|
35
|
+
STDERR.puts "WARNING: Could not capture system screenshot: #{e}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def capture_html_snapshot
|
40
|
+
# Skipping HTML Snapshot retrieval, if there is no current Selenium session
|
41
|
+
return unless @selenium_driver.session_started?
|
42
|
+
|
43
|
+
html = @selenium_driver.get_html_source
|
44
|
+
File.open(@file_path_strategy.file_path_for_html_capture(@example), "w") { |f| f.write html }
|
45
|
+
end
|
46
|
+
|
47
|
+
def capture_system_screenshot
|
48
|
+
@selenium_driver.window_maximize if @selenium_driver.session_started?
|
49
|
+
|
50
|
+
encodedImage = @selenium_driver.capture_screenshot_to_string
|
51
|
+
pngImage = Base64.decode64(encodedImage)
|
52
|
+
File.open(@file_path_strategy.file_path_for_system_screenshot(@example), "w") { |f| f.write pngImage }
|
53
|
+
end
|
54
|
+
|
55
|
+
def capture_page_screenshot
|
56
|
+
return unless @selenium_driver.chrome_backend? && @selenium_driver.session_started?
|
57
|
+
|
58
|
+
encodedImage = @selenium_driver.capture_entire_page_screenshot_to_string("")
|
59
|
+
pngImage = Base64.decode64(encodedImage)
|
60
|
+
File.open(@file_path_strategy.file_path_for_page_screenshot(@example), "w") { |f| f.write pngImage }
|
61
|
+
end
|
62
|
+
|
63
|
+
def retrieve_remote_control_logs
|
64
|
+
logs = @selenium_driver.retrieve_last_remote_control_logs
|
65
|
+
File.open(@file_path_strategy.file_path_for_remote_control_logs(@example), "w") { |f| f.write logs }
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec/example/example_group'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Monkey-patch RSpec Example Group so that we can track whether an
|
5
|
+
# example already failed or not in an after(:each) block
|
6
|
+
#
|
7
|
+
# useful to only capture Selenium screenshots when a test fails
|
8
|
+
#
|
9
|
+
# Only changed execution_error to be an instance variable (in lieu of
|
10
|
+
# a local variable).
|
11
|
+
#
|
12
|
+
module Spec
|
13
|
+
module Example
|
14
|
+
|
15
|
+
class ExampleGroup
|
16
|
+
attr_reader :execution_error
|
17
|
+
|
18
|
+
def execute(options, instance_variables)
|
19
|
+
options.reporter.example_started(self)
|
20
|
+
set_instance_variables_from_hash(instance_variables)
|
21
|
+
|
22
|
+
@execution_error = nil
|
23
|
+
Timeout.timeout(options.timeout) do
|
24
|
+
begin
|
25
|
+
before_example
|
26
|
+
run_with_description_capturing
|
27
|
+
rescue Exception => e
|
28
|
+
@execution_error ||= e
|
29
|
+
end
|
30
|
+
begin
|
31
|
+
after_example
|
32
|
+
rescue Exception => e
|
33
|
+
@execution_error ||= e
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
options.reporter.example_finished(self, @execution_error)
|
38
|
+
success = @execution_error.nil? || ExamplePendingError === @execution_error
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'base64'
|
4
|
+
require 'fileutils'
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + "/rspec_extensions")
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + "/reporting/selenium_test_report_formatter")
|
7
|
+
|
8
|
+
Spec::Runner.configure do |config|
|
9
|
+
|
10
|
+
config.after(:each) do
|
11
|
+
begin
|
12
|
+
Selenium::RSpec::SeleniumTestReportFormatter.capture_system_state(selenium_driver, self) if execution_error
|
13
|
+
if selenium_driver.session_started?
|
14
|
+
selenium_driver.set_context "Ending example '#{self.description}'"
|
15
|
+
end
|
16
|
+
rescue Exception => e
|
17
|
+
STDERR.puts "Problem while capturing system state" + e
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
class TCPSocket
|
5
|
+
|
6
|
+
def self.wait_for_service(options)
|
7
|
+
socket = nil
|
8
|
+
Timeout::timeout(options[:timeout] || 20) do
|
9
|
+
loop do
|
10
|
+
begin
|
11
|
+
socket = TCPSocket.new(options[:host], options[:port])
|
12
|
+
return
|
13
|
+
rescue Errno::ECONNREFUSED
|
14
|
+
puts ".\n"
|
15
|
+
sleep 2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
ensure
|
20
|
+
socket.close unless socket.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|