saucelabs-adapter 0.7.6
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.
- data/LICENSE +20 -0
- data/README.markdown +150 -0
- data/Rakefile +108 -0
- data/VERSION +1 -0
- data/generators/saucelabs_adapter/saucelabs_adapter_generator.rb +49 -0
- data/generators/saucelabs_adapter/templates/jsunit.rake +62 -0
- data/generators/saucelabs_adapter/templates/jsunit_suite_example.rb +19 -0
- data/generators/saucelabs_adapter/templates/saucelabs_adapter.rake +53 -0
- data/generators/saucelabs_adapter/templates/selenium.yml +66 -0
- data/lib/saucelabs-adapter.rb +1 -0
- data/lib/saucelabs_adapter.rb +6 -0
- data/lib/saucelabs_adapter/jsunit_selenium_support.rb +121 -0
- data/lib/saucelabs_adapter/run_utils.rb +14 -0
- data/lib/saucelabs_adapter/sauce_tunnel.rb +122 -0
- data/lib/saucelabs_adapter/selenium_config.rb +152 -0
- data/lib/saucelabs_adapter/test_unit_adapter.rb +49 -0
- data/lib/saucerest-ruby/gateway.rb +35 -0
- data/lib/saucerest-ruby/saucerest.rb +56 -0
- data/spec/selenium_config_spec.rb +151 -0
- data/test/helper.rb +10 -0
- data/test/test_saucelabs-adapter.rb +10 -0
- metadata +124 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
local: &local
|
2
|
+
selenium_server_address: "127.0.0.1"
|
3
|
+
selenium_server_port: "4444"
|
4
|
+
selenium_browser_key: "*chrome /Applications/Firefox.app/Contents/MacOS/firefox-bin"
|
5
|
+
application_address: "127.0.0.1"
|
6
|
+
application_port: "4000"
|
7
|
+
|
8
|
+
local_jsunit:
|
9
|
+
<<: *local
|
10
|
+
application_port: "8080"
|
11
|
+
|
12
|
+
# Possible Sauce Labs configurations as of 2009/11/19
|
13
|
+
# From: http://saucelabs.com/products/docs/sauce-ondemand/browsers
|
14
|
+
#
|
15
|
+
# saucelabs_browser_os saucelabs_browser saucelabs_browser_version (pick one)
|
16
|
+
#
|
17
|
+
# "Windows 2003" "iexplore" "6.", "7.", "8."
|
18
|
+
# "firefox" "2.", "3.0", "3.5"
|
19
|
+
# "safari" "3.", "4."
|
20
|
+
# "opera" "9."
|
21
|
+
# "googlechrome" ""
|
22
|
+
# "Linux" "firefox" "3."
|
23
|
+
saucelabs: &saucelabs
|
24
|
+
# URL of Selenium RC server:
|
25
|
+
selenium_server_address: "saucelabs.com"
|
26
|
+
selenium_server_port: "4444"
|
27
|
+
# Saucelabs credentials / Browser to drive
|
28
|
+
saucelabs_username: "YOUR-SAUCELABS-USERNAME"
|
29
|
+
saucelabs_access_key: "YOUR-SAUCELABS-ACCESS-KEY"
|
30
|
+
saucelabs_browser_os: "Linux"
|
31
|
+
saucelabs_browser: "firefox"
|
32
|
+
saucelabs_browser_version: "3."
|
33
|
+
saucelabs_max_duration_seconds: 1800
|
34
|
+
# Selenium RC browser connects to and tests the app at this URL:
|
35
|
+
application_address: "this will be ovewritten if tunnel_method == :saucetunnel"
|
36
|
+
application_port: 80
|
37
|
+
# App host is actually a tunnel that tunnels from <application_address>:<application_port> to localhost:<tunnel_to_localhost_port>
|
38
|
+
tunnel_method: :saucetunnel
|
39
|
+
tunnel_to_localhost_port: 4000 # Warning: application_port and tunnel_to_localhost_port must be identical if you are using Webrat
|
40
|
+
tunnel_startup_timeout: 240
|
41
|
+
|
42
|
+
saucelabs_jsunit: &saucelabs_jsunit
|
43
|
+
<<: *saucelabs
|
44
|
+
# We are using the Jetty server for Saucelabs JsUnit selenium testing.
|
45
|
+
localhost_app_server_port: "8080"
|
46
|
+
|
47
|
+
saucelabs_jsunit_firefox:
|
48
|
+
<<: *saucelabs_jsunit
|
49
|
+
|
50
|
+
saucelabs_jsunit_ie:
|
51
|
+
<<: *saucelabs_jsunit
|
52
|
+
saucelabs_browser_os: "Windows 2003"
|
53
|
+
saucelabs_browser: "iexplore"
|
54
|
+
saucelabs_browser_version: "7."
|
55
|
+
|
56
|
+
saucelabs_jsunit_safari:
|
57
|
+
<<: *saucelabs_jsunit
|
58
|
+
saucelabs_browser_os: "Windows 2003"
|
59
|
+
saucelabs_browser: "safari"
|
60
|
+
saucelabs_browser_version: "4."
|
61
|
+
|
62
|
+
saucelabs_jsunit_chrome:
|
63
|
+
<<: *saucelabs_jsunit
|
64
|
+
saucelabs_browser_os: "Windows 2003"
|
65
|
+
saucelabs_browser: "googlechrome"
|
66
|
+
saucelabs_browser_version: ""
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'saucelabs_adapter'
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module SaucelabsAdapter
|
2
|
+
module JsunitSeleniumSupport
|
3
|
+
|
4
|
+
def requires
|
5
|
+
require 'saucelabs_adapter/run_utils'
|
6
|
+
require "selenium/client"
|
7
|
+
require 'lsof'
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup_jsunit_selenium(options = {})
|
11
|
+
requires
|
12
|
+
@selenium_config = SeleniumConfig.new(ENV['SELENIUM_ENV'])
|
13
|
+
start_app_server(options)
|
14
|
+
@selenium_driver = @selenium_config.create_driver(options)
|
15
|
+
puts "[JsunitSeleniumSupport] calling @selenium_driver.start" if options[:debug]
|
16
|
+
@selenium_driver.start
|
17
|
+
puts "[JsunitSeleniumSupport] @selenium_driver.start done" if options[:debug]
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown_jsunit_selenium
|
21
|
+
@selenium_driver.stop
|
22
|
+
stop_app_server
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_jsunit_test(jsunit_params, options = {})
|
26
|
+
if $:.detect{ |x| x =~ /Selenium/}
|
27
|
+
raise 'Selenium gem should not be in path! (deprecated in favor of selenium-client, which we require)'
|
28
|
+
end
|
29
|
+
|
30
|
+
default_jsunit_params = {
|
31
|
+
:testPage => "/jsunit/javascripts/test-pages/suite.html",
|
32
|
+
:autorun => "true",
|
33
|
+
:setupPageTimeout => "60",
|
34
|
+
:pageLoadTimeout => "60",
|
35
|
+
:suppressCacheBuster => (@selenium_config.selenium_server_address == 'saucelabs.com').to_s
|
36
|
+
}
|
37
|
+
jsunit_params.reverse_merge!(default_jsunit_params)
|
38
|
+
|
39
|
+
test_url = "/jsunit/javascripts/jsunit/jsunit/testRunner.html?" + jsunit_params.map { |k,v| "#{k}=#{v}" }.join("&")
|
40
|
+
run_suite(@selenium_driver, test_url, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def pid_file
|
46
|
+
prepare_pid_file("#{RAILS_ROOT}/tmp/pids", "mongrel_selenium.pid")
|
47
|
+
end
|
48
|
+
|
49
|
+
def prepare_pid_file(file_path, pid_file_name)
|
50
|
+
FileUtils.mkdir_p File.expand_path(file_path)
|
51
|
+
File.expand_path("#{file_path}/#{pid_file_name}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def local_app_server_port
|
55
|
+
@selenium_config.tunnel_to_localhost_port || @selenium_config.application_port
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_app_server(options = {})
|
59
|
+
stop_app_server
|
60
|
+
puts "[JsunitSeleniumSupport] starting application server:"
|
61
|
+
app_server_logfile_path = options[:app_server_logfile_path] || "#{RAILS_ROOT}/log/jsunit_jetty_app_server.log"
|
62
|
+
RunUtils.run "ant -f #{RAILS_ROOT}/public/javascripts/jsunit/jsunit/build.xml start_server " +
|
63
|
+
"-Dport=#{local_app_server_port} " +
|
64
|
+
"-DcustomJsUnitJarPath=#{RAILS_ROOT}/public/javascripts/jsunit/jsunit_jar/jsunit.jar " +
|
65
|
+
"-DresourceBase=#{RAILS_ROOT}/public >> #{app_server_logfile_path} 2>&1 &"
|
66
|
+
end
|
67
|
+
|
68
|
+
def stop_app_server
|
69
|
+
raise "oops don't know port app server is running on" unless local_app_server_port
|
70
|
+
while Lsof.running?(local_app_server_port)
|
71
|
+
puts "Killing app server at #{local_app_server_port}..."
|
72
|
+
Lsof.kill(local_app_server_port)
|
73
|
+
sleep 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def run_suite(selenium_driver, suite_path, options = {})
|
78
|
+
default_options = {
|
79
|
+
:timeout_in_seconds => 1200
|
80
|
+
}
|
81
|
+
options.reverse_merge!(default_options)
|
82
|
+
|
83
|
+
selenium_driver.open(suite_path)
|
84
|
+
|
85
|
+
# It would be nice if this worked, but it doesn't (it returns nil even though 'Done' is not in the element).
|
86
|
+
# selenium.wait_for_condition(
|
87
|
+
# "new RegExp('Done').test(window.mainFrame.mainStatus.document.getElementById('content').innerHTML)")
|
88
|
+
|
89
|
+
tests_completed = false
|
90
|
+
begin_time = Time.now
|
91
|
+
status = ""
|
92
|
+
puts "[JsunitSeleniumSupport] Starting to poll JsUnit..." if options[:verbose]
|
93
|
+
while (Time.now - begin_time) < options[:jsunit_suite_timeout_seconds] && !tests_completed
|
94
|
+
sleep 5
|
95
|
+
status = selenium_driver.js_eval("window.mainFrame.mainStatus.document.getElementById('content').innerHTML")
|
96
|
+
status.gsub!(/^<[bB]>Status:<\/[bB]> /, '')
|
97
|
+
# Long form: window.frames['mainFrame'].frames['mainCounts'].frames['mainCountsRuns'].document.getElementById('content').innerHTML
|
98
|
+
runs = selenium_driver.js_eval("window.mainFrame.mainCounts.mainCountsRuns.document.getElementById('content').innerHTML").strip
|
99
|
+
fails = selenium_driver.js_eval("window.mainFrame.mainCounts.mainCountsFailures.document.getElementById('content').innerHTML").strip
|
100
|
+
errors = selenium_driver.js_eval("window.mainFrame.mainCounts.mainCountsErrors.document.getElementById('content').innerHTML").strip
|
101
|
+
run_count = runs.match(/\d+$/)[0].to_i
|
102
|
+
fail_count = fails.match(/\d+$/)[0].to_i
|
103
|
+
error_count = errors.match(/\d+$/)[0].to_i
|
104
|
+
puts "[JsunitSeleniumSupport] runs/fails/errors: #{run_count}/#{fail_count}/#{error_count} status: #{status}" if options[:verbose]
|
105
|
+
if status =~ /^Done /
|
106
|
+
tests_completed = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
raise "[JsunitSeleniumSupport] Tests failed to complete after #{options[:jsunit_suite_timeout_seconds]}, status was '#{status}'" unless tests_completed
|
110
|
+
|
111
|
+
puts "[JsunitSeleniumSupport] ********** JSUnit tests complete, Runs: #{run_count}, Fails: #{fail_count}, Errors: #{error_count} **********"
|
112
|
+
|
113
|
+
if (fail_count + error_count > 0)
|
114
|
+
error_messages = selenium_driver.js_eval("window.mainFrame.mainErrors.document.getElementsByName('problemsList')[0].innerHTML")
|
115
|
+
puts "[JsunitSeleniumSupport] Error messages: #{error_messages}"
|
116
|
+
end
|
117
|
+
|
118
|
+
(fail_count + error_count) == 0
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class RunUtils
|
2
|
+
def self.run(command, options = {})
|
3
|
+
default_options = {
|
4
|
+
:raise_on_fail => true
|
5
|
+
}
|
6
|
+
options.reverse_merge!(default_options)
|
7
|
+
puts "Executing: #{command}"
|
8
|
+
success = system(command)
|
9
|
+
if !success && options[:raise_on_fail]
|
10
|
+
raise "Command failed: #{command}"
|
11
|
+
end
|
12
|
+
success
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'net/ssh/gateway'
|
3
|
+
require 'saucerest-ruby/saucerest'
|
4
|
+
require 'saucerest-ruby/gateway'
|
5
|
+
|
6
|
+
module SaucelabsAdapter
|
7
|
+
class SauceTunnel
|
8
|
+
DEFAULT_TUNNEL_STARTUP_TIMEOUT = 240
|
9
|
+
|
10
|
+
def initialize(se_config)
|
11
|
+
raise "SauceTunnel.new requires a SeleniumConfig argument" unless se_config.is_a?(SeleniumConfig)
|
12
|
+
@se_config = se_config
|
13
|
+
connect_to_rest_api
|
14
|
+
start_tunnel
|
15
|
+
end
|
16
|
+
|
17
|
+
def start_tunnel
|
18
|
+
say "Setting up tunnel from Saucelabs (#{@se_config.application_address}:#{@se_config.application_port}) to localhost:#{@se_config.tunnel_to_localhost_port} (timeout #{tunnel_startup_timeout}s)..."
|
19
|
+
boot_tunnel_machine
|
20
|
+
setup_ssh_reverse_tunnel
|
21
|
+
# WARNING: JsUnit depends upon the format of this output line:
|
22
|
+
say "Tunnel ID #{@tunnel_id} for #{@se_config.application_address} is up."
|
23
|
+
end
|
24
|
+
|
25
|
+
def tunnel_startup_timeout
|
26
|
+
(@se_config.tunnel_startup_timeout || DEFAULT_TUNNEL_STARTUP_TIMEOUT).to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def shutdown
|
30
|
+
say "Shutting down tunnel to Saucelabs..."
|
31
|
+
teardown_ssh_reverse_tunnel
|
32
|
+
shutdown_tunnel_machine
|
33
|
+
say "done."
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def connect_to_rest_api
|
39
|
+
sauce_api_url = "https://#{@se_config.saucelabs_username}:#{@se_config.saucelabs_access_key}@saucelabs.com/rest/#{@se_config.saucelabs_username}/"
|
40
|
+
debug "Connecting to Sauce API at #{sauce_api_url}"
|
41
|
+
@sauce_api_endpoint = SauceREST::Client.new sauce_api_url
|
42
|
+
end
|
43
|
+
|
44
|
+
def boot_tunnel_machine
|
45
|
+
debug "Booting tunnel host:"
|
46
|
+
response = @sauce_api_endpoint.create(:tunnel, 'DomainNames' => [@se_config.application_address])
|
47
|
+
if response.has_key? 'error'
|
48
|
+
raise "Error booting tunnel machine: " + response['error']
|
49
|
+
end
|
50
|
+
@tunnel_id = response['id']
|
51
|
+
debug "Tunnel id: %s" % @tunnel_id
|
52
|
+
|
53
|
+
Timeout::timeout(tunnel_startup_timeout) do
|
54
|
+
last_status = tunnel_status = nil
|
55
|
+
begin
|
56
|
+
sleep 5
|
57
|
+
@tunnel_info = @sauce_api_endpoint.get :tunnel, @tunnel_id
|
58
|
+
tunnel_status = @tunnel_info['Status']
|
59
|
+
debug " tunnel host is #{tunnel_status}" if tunnel_status != last_status
|
60
|
+
last_status = tunnel_status
|
61
|
+
case tunnel_status
|
62
|
+
when 'new', 'booting'
|
63
|
+
# Alrighty. Keep going.
|
64
|
+
when 'running'
|
65
|
+
# We're done.
|
66
|
+
when 'terminated'
|
67
|
+
raise "There was a problem booting the tunnel machine: it terminated (%s)" % @tunnel_info['Error']
|
68
|
+
else
|
69
|
+
raise "Unknown tunnel machine status: #{tunnel_status} (#{@tunnel_info.inspect})"
|
70
|
+
end
|
71
|
+
end while tunnel_status != 'running'
|
72
|
+
end
|
73
|
+
rescue Timeout::Error
|
74
|
+
error_message = "Tunnel did not come up in #{tunnel_startup_timeout} seconds."
|
75
|
+
STDERR.puts "[saucelabs-adapter] " + error_message
|
76
|
+
shutdown_tunnel_machine
|
77
|
+
raise error_message
|
78
|
+
end
|
79
|
+
|
80
|
+
def shutdown_tunnel_machine
|
81
|
+
return unless @sauce_api_endpoint && @tunnel_id
|
82
|
+
debug "Shutting down tunnel machine:"
|
83
|
+
Timeout::timeout(120) do
|
84
|
+
@sauce_api_endpoint.delete :tunnel, @tunnel_id
|
85
|
+
status = nil
|
86
|
+
begin
|
87
|
+
sleep 5
|
88
|
+
status = @sauce_api_endpoint.get(:tunnel, @tunnel_id)['Status']
|
89
|
+
debug status
|
90
|
+
end while status != 'terminated'
|
91
|
+
end
|
92
|
+
rescue Timeout::Error
|
93
|
+
# Do not raise here, or else you give false negatives from test runs
|
94
|
+
STDERR.puts "*" * 80
|
95
|
+
STDERR.puts "Sauce Tunnel failed to shut down! Go visit http://saucelabs.com/tunnels and shut down the tunnel for #{@se_config.application_address}"
|
96
|
+
STDERR.puts "*" * 80
|
97
|
+
end
|
98
|
+
|
99
|
+
def setup_ssh_reverse_tunnel
|
100
|
+
debug "Starting ssh reverse tunnel"
|
101
|
+
@gateway = Net::SSH::Gateway.new(@tunnel_info['Host'], @se_config.saucelabs_username, {:password => @se_config.saucelabs_access_key})
|
102
|
+
@port = @gateway.open_remote(@se_config.tunnel_to_localhost_port.to_i, "127.0.0.1", @se_config.application_port.to_i, "0.0.0.0")
|
103
|
+
end
|
104
|
+
|
105
|
+
def teardown_ssh_reverse_tunnel
|
106
|
+
if @gateway
|
107
|
+
debug "Shutting down ssh reverse tunnel"
|
108
|
+
@gateway.close(@port) if @port
|
109
|
+
@gateway.shutdown! if @gateway
|
110
|
+
debug "done."
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def say(what)
|
115
|
+
STDOUT.puts "[saucelabs-adapter] " + what
|
116
|
+
end
|
117
|
+
|
118
|
+
def debug(what)
|
119
|
+
STDOUT.puts "[saucelabs-adapter] " + what if ENV['SAUCELABS_ADAPTER_DEBUG']
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module SaucelabsAdapter
|
2
|
+
class SeleniumConfig
|
3
|
+
attr_reader :configuration
|
4
|
+
|
5
|
+
def initialize(configuration_name = nil, selenium_yml_path = nil)
|
6
|
+
selenium_yml_path = selenium_yml_path || File.join(RAILS_ROOT, 'config', 'selenium.yml')
|
7
|
+
SeleniumConfig.parse_yaml(selenium_yml_path)
|
8
|
+
build_configuration(configuration_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(attribute, value)
|
12
|
+
@configuration[attribute.to_s] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
[ :selenium_server_address, :selenium_server_port,
|
16
|
+
:application_address, :application_port,
|
17
|
+
:saucelabs_username, :saucelabs_access_key,
|
18
|
+
:saucelabs_browser_os, :saucelabs_browser, :saucelabs_browser_version,
|
19
|
+
:saucelabs_max_duration_seconds,
|
20
|
+
:tunnel_method, :tunnel_to_localhost_port, :tunnel_startup_timeout ].each do |attr|
|
21
|
+
define_method(attr) do
|
22
|
+
@configuration[attr.to_s]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def selenium_browser_key
|
27
|
+
if selenium_server_address == 'saucelabs.com'
|
28
|
+
# Create the JSON string that Saucelabs needs:
|
29
|
+
{ 'username' => saucelabs_username,
|
30
|
+
'access-key' => saucelabs_access_key,
|
31
|
+
'os' => saucelabs_browser_os,
|
32
|
+
'browser' => saucelabs_browser,
|
33
|
+
'browser-version' => saucelabs_browser_version,
|
34
|
+
'max-duration' => saucelabs_max_duration_seconds.to_i,
|
35
|
+
'job-name' => ENV['SAUCELABS_JOB_NAME'] || Socket.gethostname
|
36
|
+
}.to_json
|
37
|
+
else
|
38
|
+
@configuration['selenium_browser_key']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def application_address
|
43
|
+
if start_sauce_tunnel?
|
44
|
+
# We are using Sauce Labs and Sauce Tunnel.
|
45
|
+
# We need to use a masquerade hostname on the EC2 end of the tunnel that will be unique within the scope of
|
46
|
+
# this account (e.g. pivotallabs). Therefore we mint a fairly unique hostname here.
|
47
|
+
hostname = Socket.gethostname.split(".").first
|
48
|
+
"#{hostname}-#{Process.pid}.com"
|
49
|
+
else
|
50
|
+
@configuration['application_address']
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Takes a Webrat::Configuration object and configures it by calling methods on it
|
56
|
+
def configure_webrat(webrat_configuration_object)
|
57
|
+
{
|
58
|
+
'selenium_server_address' => :selenium_server_address,
|
59
|
+
'selenium_server_port' => :selenium_server_port,
|
60
|
+
'selenium_browser_key' => :selenium_browser_key,
|
61
|
+
'application_address' => :application_address,
|
62
|
+
'application_port' => :application_port
|
63
|
+
}.each do |webrat_configuration_method, our_accessor|
|
64
|
+
webrat_configuration_object.send("#{webrat_configuration_method}=", self.send(our_accessor).to_s)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Takes a Polonium::Configuration object and configures it by calling methods on it
|
69
|
+
def configure_polonium(polonium_configuration_object)
|
70
|
+
{
|
71
|
+
'selenium_server_host' => :selenium_server_address,
|
72
|
+
'selenium_server_port' => :selenium_server_port,
|
73
|
+
'browser' => :selenium_browser_key,
|
74
|
+
'external_app_server_host' => :application_address,
|
75
|
+
'external_app_server_port' => :application_port
|
76
|
+
}.each do |polonium_configuration_method, our_accessor|
|
77
|
+
polonium_configuration_object.send("#{polonium_configuration_method}=", self.send(our_accessor).to_s)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_driver(selenium_args = {}, options = {})
|
82
|
+
args = selenium_client_driver_args.merge(selenium_args)
|
83
|
+
puts "[saucelabs-adapter] Connecting to Selenium RC server at #{args[:host]}:#{args[:port]} (testing app at #{args[:url]})" if options[:debug]
|
84
|
+
puts "[saucelabs-adapter] args = #{args.inspect}" if options[:debug]
|
85
|
+
driver = ::Selenium::Client::Driver.new(args)
|
86
|
+
puts "[saucelabs-adapter] done" if options[:debug]
|
87
|
+
driver
|
88
|
+
end
|
89
|
+
|
90
|
+
def start_sauce_tunnel?
|
91
|
+
tunnel_method.to_sym == :saucetunnel
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.parse_yaml(selenium_yml_path)
|
95
|
+
raise "[saucelabs-adapter] could not open #{selenium_yml_path}" unless File.exist?(selenium_yml_path)
|
96
|
+
@@selenium_configs ||= YAML.load_file(selenium_yml_path)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def build_configuration(configuration_name)
|
102
|
+
@configuration = @@selenium_configs[configuration_name]
|
103
|
+
raise "[saucelabs-adapter] stanza '#{configuration_name}' not found in #{@selenium_yml}" unless @configuration
|
104
|
+
check_configuration(configuration_name)
|
105
|
+
end
|
106
|
+
|
107
|
+
def check_configuration(configuration_name)
|
108
|
+
errors = []
|
109
|
+
errors << require_attributes([:selenium_server_address, :selenium_server_port, :application_port])
|
110
|
+
if selenium_server_address == 'saucelabs.com'
|
111
|
+
errors << require_attributes([ :saucelabs_username, :saucelabs_access_key,
|
112
|
+
:saucelabs_browser_os, :saucelabs_browser, :saucelabs_browser_version,
|
113
|
+
:saucelabs_max_duration_seconds ],
|
114
|
+
"when selenium_server_address is saucelabs.com")
|
115
|
+
case tunnel_method.to_sym
|
116
|
+
when nil, ""
|
117
|
+
when :saucetunnel, :othertunnel
|
118
|
+
errors << require_attributes([:tunnel_to_localhost_port ],
|
119
|
+
"if tunnel_method is set")
|
120
|
+
else
|
121
|
+
errors << "Unknown tunnel_method: #{tunnel_method}"
|
122
|
+
end
|
123
|
+
else
|
124
|
+
errors << require_attributes([:selenium_browser_key, :application_address ],
|
125
|
+
"unless server is saucelab.com")
|
126
|
+
end
|
127
|
+
|
128
|
+
errors.flatten!.compact!
|
129
|
+
if !errors.empty?
|
130
|
+
raise "[saucelabs-adapter] Aborting; stanza #{configuration_name} has the following errors:\n\t" + errors.join("\n\t")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def require_attributes(names, under_what_circumstances = "")
|
135
|
+
errors = []
|
136
|
+
names.each do |attribute|
|
137
|
+
errors << "#{attribute} is required #{under_what_circumstances}" if send(attribute).nil?
|
138
|
+
end
|
139
|
+
errors
|
140
|
+
end
|
141
|
+
|
142
|
+
def selenium_client_driver_args
|
143
|
+
{
|
144
|
+
:host => selenium_server_address,
|
145
|
+
:port => selenium_server_port.to_s,
|
146
|
+
:browser => selenium_browser_key,
|
147
|
+
:url => "http://#{application_address}:#{application_port}",
|
148
|
+
:timeout_in_seconds => 600
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|