warnold-selenium-client 1.2.19

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.
Files changed (33) hide show
  1. data/README.markdown +338 -0
  2. data/examples/rspec/google_spec.rb +41 -0
  3. data/examples/script/google.rb +25 -0
  4. data/examples/testunit/google_test.rb +39 -0
  5. data/lib/nautilus/shell.rb +34 -0
  6. data/lib/selenium.rb +14 -0
  7. data/lib/selenium/client.rb +27 -0
  8. data/lib/selenium/client/base.rb +118 -0
  9. data/lib/selenium/client/driver.rb +11 -0
  10. data/lib/selenium/client/extensions.rb +118 -0
  11. data/lib/selenium/client/idiomatic.rb +488 -0
  12. data/lib/selenium/client/javascript_expression_builder.rb +116 -0
  13. data/lib/selenium/client/javascript_frameworks/jquery.rb +13 -0
  14. data/lib/selenium/client/javascript_frameworks/prototype.rb +13 -0
  15. data/lib/selenium/client/legacy_driver.rb +1711 -0
  16. data/lib/selenium/client/protocol.rb +104 -0
  17. data/lib/selenium/client/selenium_helper.rb +34 -0
  18. data/lib/selenium/command_error.rb +4 -0
  19. data/lib/selenium/protocol_error.rb +4 -0
  20. data/lib/selenium/rake/default_tasks.rb +17 -0
  21. data/lib/selenium/rake/remote_control_start_task.rb +71 -0
  22. data/lib/selenium/rake/remote_control_stop_task.rb +44 -0
  23. data/lib/selenium/rake/tasks.rb +6 -0
  24. data/lib/selenium/remote_control/remote_control.rb +35 -0
  25. data/lib/selenium/rspec/reporting/file_path_strategy.rb +78 -0
  26. data/lib/selenium/rspec/reporting/html_report.rb +155 -0
  27. data/lib/selenium/rspec/reporting/selenium_test_report_formatter.rb +88 -0
  28. data/lib/selenium/rspec/reporting/system_capture.rb +72 -0
  29. data/lib/selenium/rspec/rspec_extensions.rb +104 -0
  30. data/lib/selenium/rspec/spec_helper.rb +34 -0
  31. data/lib/tcp_socket_extension.rb +32 -0
  32. data/test/all_unit_tests.rb +3 -0
  33. metadata +102 -0
@@ -0,0 +1,104 @@
1
+ module Selenium
2
+ module Client
3
+
4
+ HTTP_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8' }
5
+
6
+ # Module in charge of handling Selenium over-the-wire HTTP protocol
7
+ module Protocol
8
+ attr_reader :session_id
9
+
10
+ def remote_control_command(verb, args=[])
11
+ timeout(@default_timeout_in_seconds) do
12
+ status, response = http_post(http_request_for(verb, args))
13
+ raise Selenium::CommandError, response unless status == "OK"
14
+ response[3..-1] # strip "OK," from response
15
+ end
16
+ end
17
+
18
+ def string_command(verb, args=[])
19
+ remote_control_command(verb, args)
20
+ end
21
+
22
+ def string_array_command(verb, args=[])
23
+ csv = string_command(verb, args)
24
+ token = ""
25
+ tokens = []
26
+ escape = false
27
+ csv.split(//).each do |letter|
28
+ if escape
29
+ token += letter
30
+ escape = false
31
+ next
32
+ end
33
+ case letter
34
+ when '\\'
35
+ escape = true
36
+ when ','
37
+ tokens << token
38
+ token = ""
39
+ else
40
+ token += letter
41
+ end
42
+ end
43
+ tokens << token
44
+ return tokens
45
+ end
46
+
47
+ def number_command(verb, args)
48
+ string_command verb, args
49
+ end
50
+
51
+ def number_array_command(verb, args)
52
+ string_array_command verb, args
53
+ end
54
+
55
+ def boolean_command(verb, args=[])
56
+ parse_boolean_value string_command(verb, args)
57
+ end
58
+
59
+ def boolean_array_command(verb, args)
60
+ string_array_command(verb, args).collect {|value| parse_boolean_value(value)}
61
+ end
62
+
63
+ protected
64
+
65
+ def parse_boolean_value(value)
66
+ if ("true" == value)
67
+ return true
68
+ elsif ("false" == value)
69
+ return false
70
+ end
71
+ raise ProtocolError, "Invalid Selenese boolean value that is neither 'true' nor 'false': got '#{value}'"
72
+ end
73
+
74
+ def http_request_for(verb, args)
75
+ data = "cmd=#{CGI::escape(verb)}"
76
+ args.each_with_index do |arg, index|
77
+ data << "&#{index.succ}=#{CGI::escape(arg.to_s)}"
78
+ end
79
+ data << "&sessionId=#{session_id}" unless session_id.nil?
80
+ data
81
+ end
82
+
83
+ def http_post(data)
84
+ start = Time.now
85
+ called_from = caller.detect{|line| line !~ /(selenium-client|vendor|usr\/lib\/ruby|\(eval\))/i}
86
+ http = Net::HTTP.new(@host, @port)
87
+ http.open_timeout = default_timeout_in_seconds
88
+ http.read_timeout = default_timeout_in_seconds
89
+ response = http.post('/selenium-server/driver/', data, HTTP_HEADERS)
90
+ if response.body !~ /^OK/
91
+ puts "#{start} selenium-client received failure from selenium server:"
92
+ puts "requested:"
93
+ puts "\t" + CGI::unescape(data.split('&').join("\n\t"))
94
+ puts "received:"
95
+ puts "\t#{response.body.inspect}"
96
+ puts "\tcalled from #{called_from}"
97
+ end
98
+ [ response.body[0..1], response.body ]
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,34 @@
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 default open method to actually delegates to @selenium
11
+ def open(url)
12
+ @selenium.open url
13
+ end
14
+
15
+ # Overrides default type method to actually delegates to @selenium
16
+ def type(locator, value)
17
+ @selenium.type locator, value
18
+ end
19
+
20
+ # Overrides default select method to actually delegates to @selenium
21
+ def select(input_locator, option_locator)
22
+ @selenium.select input_locator, option_locator
23
+ end
24
+
25
+ # Delegates to @selenium on method missing
26
+ def method_missing(method_name, *args)
27
+ return super unless @selenium.respond_to?(method_name)
28
+
29
+ @selenium.send(method_name, *args)
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ module Selenium
2
+ class CommandError < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Selenium
2
+ class ProtocolError < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ require 'selenium/rake/tasks'
2
+
3
+ Selenium::Rake::RemoteControlStartTask.new do |rc|
4
+ rc.timeout_in_seconds = 3 * 60
5
+ rc.background = true
6
+ rc.nohup = false
7
+ rc.wait_until_up_and_running = true
8
+ rc.additional_args << "-singleWindow"
9
+ end
10
+
11
+ Selenium::Rake::RemoteControlStopTask.new
12
+
13
+ desc "Restart Selenium Remote Control"
14
+ task :'selenium:rc:restart' do
15
+ Rake::Task[:'selenium:rc:stop'].execute [] rescue nil
16
+ Rake::Task[:'selenium:rc:start'].execute []
17
+ end
@@ -0,0 +1,71 @@
1
+ module Selenium
2
+ module Rake
3
+
4
+ # Rake tasks to start a Remote Control server.
5
+ #
6
+ # require 'selenium/rake/tasks'
7
+ #
8
+ # Selenium::Rake::RemoteControlStartTask.new do |rc|
9
+ # rc.port = 4444
10
+ # rc.timeout_in_seconds = 3 * 60
11
+ # rc.background = true
12
+ # rc.wait_until_up_and_running = true
13
+ # rc.jar_file = "/path/to/where/selenium-rc-standalone-jar-is-installed"
14
+ # rc.additional_args << "-singleWindow"
15
+ # end
16
+ #
17
+ # If you do not explicitly specify the path to selenium remote control jar
18
+ # it will be "auto-discovered" in `vendor` directory using the following
19
+ # path : `vendor/selenium-remote-control/selenium-server*-standalone.jar`
20
+ #
21
+ # To leverage the latest selenium-client capabilities, you may need to download
22
+ # a recent nightly build of a standalone packaging of Selenium Remote
23
+ # Control. You will find the nightly build at
24
+ # http://nexus.openqa.org/content/repositories/snapshots/org/seleniumhq/selenium/server/selenium-server/
25
+ class RemoteControlStartTask
26
+ attr_accessor :port, :timeout_in_seconds, :background,
27
+ :wait_until_up_and_running, :additional_args,
28
+ :log_to, :nohup
29
+ attr_reader :jar_file
30
+
31
+ JAR_FILE_PATTERN = "vendor/selenium-remote-control/selenium-server-*.jar"
32
+
33
+ def initialize(name = :'selenium:rc:start')
34
+ @name = name
35
+ @port = 4444
36
+ @timeout_in_seconds = 5
37
+ project_specific_jar = Dir[JAR_FILE_PATTERN].first
38
+ @jar_file = project_specific_jar
39
+ @additional_args = []
40
+ @background = false
41
+ @nohup = false
42
+ @wait_until_up_and_running = false
43
+ yield self if block_given?
44
+ define
45
+ end
46
+
47
+ def jar_file=(new_jar_file)
48
+ @jar_file = File.expand_path(new_jar_file)
49
+ end
50
+
51
+ def define
52
+ desc "Launch Selenium Remote Control"
53
+ task @name do
54
+ puts "Starting Selenium Remote Control at 0.0.0.0:#{@port}..."
55
+ raise "Could not find jar file '#{@jar_file}'. Expected it under #{JAR_FILE_PATTERN}" unless @jar_file && File.exists?(@jar_file)
56
+ remote_control = Selenium::RemoteControl::RemoteControl.new("0.0.0.0", @port, :timeout => @timeout_in_seconds)
57
+ remote_control.jar_file = @jar_file
58
+ remote_control.additional_args = @additional_args
59
+ remote_control.log_to = @log_to
60
+ remote_control.start :background => @background, :nohup => @nohup
61
+ if @background && @wait_until_up_and_running
62
+ puts "Waiting for Remote Control to be up and running..."
63
+ TCPSocket.wait_for_service :host => @host, :port => @port
64
+ end
65
+ puts "Selenium Remote Control at 0.0.0.0:#{@port} ready"
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,44 @@
1
+ module Selenium
2
+ module Rake
3
+
4
+ # Rake task to stop a Selenium Remote Control Server
5
+ #
6
+ # Selenium::Rake::RemoteControlStopTask.new do |rc|
7
+ # rc.host = "localhost"
8
+ # rc.port = 4444
9
+ # rc.timeout_in_seconds = 3 * 60
10
+ # end
11
+ #
12
+ class RemoteControlStopTask
13
+ attr_accessor :host, :port, :timeout_in_seconds, :wait_until_stopped,
14
+ :shutdown_command
15
+
16
+ def initialize(name = :'selenium:rc:stop')
17
+ @host = "localhost"
18
+ @name = name
19
+ @port = 4444
20
+ @timeout_in_seconds = nil
21
+ @shutdown_command = nil
22
+ @wait_until_stopped = true
23
+ yield self if block_given?
24
+ define
25
+ end
26
+
27
+ def define
28
+ desc "Stop Selenium Remote Control running"
29
+ task @name do
30
+ puts "Stopping Selenium Remote Control running at #{host}:#{port}..."
31
+ remote_control = Selenium::RemoteControl::RemoteControl.new(
32
+ host, port, :timeout => timeout_in_seconds,
33
+ :shutdown_command => shutdown_command)
34
+ remote_control.stop
35
+ if @wait_until_stopped
36
+ TCPSocket.wait_for_service_termination :host => host, :port => port
37
+ end
38
+ puts "Stopped Selenium Remote Control running at #{host}:#{port}"
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,6 @@
1
+ require "net/http"
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../nautilus/shell')
3
+ require File.expand_path(File.dirname(__FILE__) + '/../../tcp_socket_extension')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../remote_control/remote_control')
5
+ require File.expand_path(File.dirname(__FILE__) + '/remote_control_start_task')
6
+ require File.expand_path(File.dirname(__FILE__) + '/remote_control_stop_task')
@@ -0,0 +1,35 @@
1
+ module Selenium
2
+ module RemoteControl
3
+
4
+ class RemoteControl
5
+ attr_reader :host, :port, :timeout_in_seconds, :firefox_profile, :shutdown_command
6
+ attr_accessor :additional_args, :jar_file, :log_to
7
+
8
+ def initialize(host, port, options={})
9
+ @host, @port = host, port
10
+ @timeout_in_seconds = options[:timeout] || (2 * 60)
11
+ @shutdown_command = options[:shutdown_command] || "shutDownSeleniumServer"
12
+ @firefox_profile = options[:firefox_profile]
13
+ @additional_args = options[:additional_args] || []
14
+ @shell = Nautilus::Shell.new
15
+ end
16
+
17
+ def start(options = {})
18
+ command = "java -jar \"#{jar_file}\""
19
+ command << " -port #{@port}"
20
+ command << " -timeout #{@timeout_in_seconds}"
21
+ command << " -firefoxProfileTemplate '#{@firefox_profile}'" if @firefox_profile
22
+ command << " #{additional_args.join(' ')}" unless additional_args.empty?
23
+ command << " > #{log_to}" if log_to
24
+
25
+ @shell.run command, {:background => options[:background], :nohup => options[:nohup]}
26
+ end
27
+
28
+ def stop
29
+ Net::HTTP.get(@host, "/selenium-server/driver/?cmd=#{shutdown_command}", @port)
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,78 @@
1
+ module Selenium
2
+ module RSpec
3
+ module Reporting
4
+
5
+ class FilePathStrategy
6
+ attr_reader :final_report_file_path
7
+
8
+ REPORT_DEFAULT_FILE_PATH = File.join(Dir::tmpdir, "selenium_test_report", "index.html")
9
+
10
+ def initialize(final_report_file_path)
11
+ @final_report_file_path = final_report_file_path || REPORT_DEFAULT_FILE_PATH
12
+ @relative_dir = nil
13
+ end
14
+
15
+ def base_report_dir
16
+ @base_report_dir ||= File.dirname(File.expand_path(@final_report_file_path))
17
+ end
18
+
19
+ def relative_dir
20
+ return @relative_dir if @relative_dir
21
+
22
+ file_name_without_extension = File.basename(@final_report_file_path).sub(/\.[^\.]*$/, "")
23
+ @relative_dir ||= "resources/" + file_name_without_extension
24
+ end
25
+
26
+ def relative_file_path_for_html_capture(example)
27
+ "#{relative_dir}/example_#{example.reporting_uid}.html"
28
+ end
29
+
30
+ def relative_file_path_for_system_screenshot(example)
31
+ "#{relative_dir}/example_#{example.reporting_uid}_system_screenshot.png"
32
+ end
33
+
34
+ def relative_file_path_for_page_screenshot(example)
35
+ "#{relative_dir}/example_#{example.reporting_uid}_page_screenshot.png"
36
+ end
37
+
38
+ def relative_file_path_for_remote_control_logs(example)
39
+ "#{relative_dir}/example_#{example.reporting_uid}_remote_control.log"
40
+ end
41
+
42
+ def relative_file_path_for_browser_network_traffic(example)
43
+ "#{relative_dir}/example_#{example.reporting_uid}_browser_network_traffic.log"
44
+ end
45
+
46
+ def file_path_for_html_capture(example)
47
+ file_path relative_file_path_for_html_capture(example)
48
+ end
49
+
50
+ def file_path_for_system_screenshot(example)
51
+ file_path relative_file_path_for_system_screenshot(example)
52
+ end
53
+
54
+ def file_path_for_page_screenshot(example)
55
+ file_path relative_file_path_for_page_screenshot(example)
56
+ end
57
+
58
+ def file_path_for_remote_control_logs(example)
59
+ file_path relative_file_path_for_remote_control_logs(example)
60
+ end
61
+
62
+ def file_path_for_browser_network_traffic(example)
63
+ file_path relative_file_path_for_browser_network_traffic(example)
64
+ end
65
+
66
+ def file_path(relative_file_path)
67
+ the_file_path = base_report_dir + "/" + relative_file_path
68
+ parent_dir = File.dirname(the_file_path)
69
+ FileUtils.mkdir_p(parent_dir) unless File.directory?(parent_dir)
70
+ the_file_path
71
+ end
72
+
73
+
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,155 @@
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_" + example.reporting_uid
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
+ remote_control_logs_path = @file_path_strategy.file_path_for_remote_control_logs(example)
28
+
29
+ html = ""
30
+ if File.exists? @file_path_strategy.file_path_for_html_capture(example)
31
+ html << toggable_section(dom_id, :id => "snapshot", :url=> snapshot_url, :name => "Dynamic HTML Snapshot")
32
+ end
33
+ if File.exists? remote_control_logs_path
34
+ html << toggable_inline_section(dom_id, :id => "rc_logs", :url => remote_control_logs_url, :path=> remote_control_logs_path, :name => "Remote Control Logs")
35
+ end
36
+ if File.exists? @file_path_strategy.file_path_for_page_screenshot(example)
37
+ html << toggable_image_section(dom_id, :id => "page_screenshot", :name => "Page Screenshot", :url => page_screenshot_url)
38
+ end
39
+ if File.exists? @file_path_strategy.file_path_for_system_screenshot(example)
40
+ html << toggable_image_section(dom_id, :id => "system_screenshot", :name => "System Screenshot", :url => system_screenshot_url)
41
+ end
42
+
43
+ return html
44
+ end
45
+
46
+ def self.append_javascript(global_scripts)
47
+ global_scripts + <<-EOF
48
+ function toggleVisilibility(id, description) {
49
+ var section;
50
+ var link;
51
+
52
+ section = document.getElementById(id);
53
+ link = document.getElementById(id + "_link");
54
+
55
+ if (section.style.display == "block") {
56
+ section.style.display = "none"
57
+ link.innerHTML = "Show " + description
58
+ } else {
59
+ section.style.display = "block"
60
+ link.innerHTML = "Hide " + description
61
+ }
62
+ }
63
+ EOF
64
+ end
65
+
66
+ def self.append_css(global_styles)
67
+ global_styles + <<-EOF
68
+
69
+ div.rspec-report textarea {
70
+ width: 100%;
71
+ }
72
+
73
+ div.rspec-report .dyn-source {
74
+ background: #FFFFEE none repeat scroll 0%;
75
+ border:1px dotted black;
76
+ color: #000000;
77
+ display: none;
78
+ margin: 0.5em 2em;
79
+ padding: 0.5em;
80
+ }
81
+
82
+ EOF
83
+ end
84
+
85
+ def toggable_section(dom_id, options)
86
+ <<-EOS
87
+
88
+ <div>[
89
+ <a id="#{dom_id}_#{options[:id]}_link"
90
+ href=\"javascript:toggleVisilibility('#{dom_id}_#{options[:id]}', '#{options[:name]}')\">Show #{options[:name]}</a>
91
+ ]</div>
92
+ <br/><br/>
93
+ <div id="#{dom_id}_#{options[:id]}" class="dyn-source">
94
+ <a href="#{options[:url]}">Full screen</a><br/><br/>
95
+ <iframe src="#{options[:url]}" width="100%" height="600px" ></iframe>
96
+ </div>
97
+
98
+ EOS
99
+ end
100
+
101
+ def toggable_inline_section(dom_id, options)
102
+ <<-EOS
103
+
104
+ <div>[
105
+ <a id="#{dom_id}_#{options[:id]}_link"
106
+ href=\"javascript:toggleVisilibility('#{dom_id}_#{options[:id]}', '#{options[:name]}')\">Show #{options[:name]}</a>
107
+ ]</div>
108
+ <br/><br/>
109
+ <div id="#{dom_id}_#{options[:id]}" class="dyn-source">
110
+ <a href="#{options[:url]}">Full screen</a><br/><br/>
111
+ <pre>#{File.open(options[:path]).read}</pre>
112
+ </div>
113
+
114
+ EOS
115
+ end
116
+
117
+ def toggable_image_section(dom_id, options)
118
+ <<-EOS
119
+
120
+ <div>[<a id="#{dom_id}_#{options[:id]}_link" href="javascript:toggleVisilibility('#{dom_id}_#{options[:id]}', '#{options[:name]}');">Show #{options[:name]}</a>]</div>
121
+ <br/>
122
+ <div id="#{dom_id}_#{options[:id]}" style="display: none">
123
+ <a href="#{options[:url]}">
124
+ <img width="80%" src="#{options[:url]}" />
125
+ </a>
126
+ </div>
127
+ <br/>
128
+
129
+ EOS
130
+ end
131
+
132
+ def report_header
133
+ super + "\n<script type=\"text/javascript\">moveProgressBar('100.0');</script>"
134
+ end
135
+
136
+ end
137
+ end
138
+
139
+
140
+ # Tweaks raised Exceptions to mask noisy (unneeded) parts of the backtrace
141
+ class SeleniumQuietBacktraceTweaker < Spec::Runner::QuietBacktraceTweaker
142
+ unless defined?(SELENIUM_IGNORE_PATTERNS)
143
+ SELENIUM_IGNORE_PATTERNS = [
144
+ /selenium-client/,
145
+ /selenium\/client/,
146
+ ]
147
+ end
148
+
149
+ def ignored_patterns
150
+ super + SELENIUM_IGNORE_PATTERNS
151
+ end
152
+ end
153
+
154
+ end
155
+ end