warnold-selenium-client 1.2.19

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