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.
@@ -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