selenium-client 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|