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