selenium-client 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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