jsunit-sauce 0.0.1
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 +19 -0
- data/README.md +1 -0
- data/Rakefile +11 -0
- data/Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit/run_utils.rb +18 -0
- data/Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit/selenium_config.rb +220 -0
- data/Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit/utilities.rb +69 -0
- data/Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit.rb +134 -0
- data/Users/epall/Dropbox/code/jsunit-sauce/test/helper.rb +5 -0
- metadata +107 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Sauce Labs Inc
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
JSUnit glue for running your JSUnit tests in Sauce OnDemand
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Sauce
|
2
|
+
module JSUnit
|
3
|
+
class RunUtils
|
4
|
+
def self.run(command, options = {})
|
5
|
+
default_options = {
|
6
|
+
:raise_on_fail => true
|
7
|
+
}
|
8
|
+
options = default_options.merge(options)
|
9
|
+
puts "Executing: #{command}"
|
10
|
+
success = system(command)
|
11
|
+
if !success && options[:raise_on_fail]
|
12
|
+
raise "Command failed: #{command}"
|
13
|
+
end
|
14
|
+
success
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
module Sauce
|
2
|
+
module JSUnit
|
3
|
+
class SeleniumConfig
|
4
|
+
|
5
|
+
include Utilities
|
6
|
+
|
7
|
+
attr_reader :configuration
|
8
|
+
|
9
|
+
def initialize(configuration_name = nil, selenium_yml_path = nil)
|
10
|
+
selenium_yml_path = selenium_yml_path || File.join(ENV['RAILS_ROOT'] || RAILS_ROOT, 'config', 'selenium.yml')
|
11
|
+
SeleniumConfig.parse_yaml(selenium_yml_path)
|
12
|
+
build_configuration(configuration_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(attribute, value)
|
16
|
+
@configuration[attribute.to_s] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
[ :test_framework, :start_server,
|
20
|
+
:selenium_server_address, :selenium_server_port,
|
21
|
+
:application_address, :application_port,
|
22
|
+
:saucelabs_username, :saucelabs_access_key,
|
23
|
+
:saucelabs_browser_os, :saucelabs_browser, :saucelabs_browser_version,
|
24
|
+
:saucelabs_max_duration_seconds,
|
25
|
+
:tunnel_method, :tunnel_to_localhost_port, :tunnel_startup_timeout,
|
26
|
+
:tunnel_username, :tunnel_password, :tunnel_keyfile,
|
27
|
+
:jsunit_polling_interval_seconds, :kill_mongrel_after_suite ].each do |attr|
|
28
|
+
define_method(attr) do
|
29
|
+
@configuration[attr.to_s]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def selenium_browser_key
|
34
|
+
if selenium_server_address == 'saucelabs.com'
|
35
|
+
# Create the JSON string that Saucelabs needs:
|
36
|
+
{ 'username' => saucelabs_username,
|
37
|
+
'access-key' => saucelabs_access_key,
|
38
|
+
'os' => saucelabs_browser_os,
|
39
|
+
'browser' => saucelabs_browser,
|
40
|
+
'browser-version' => saucelabs_browser_version,
|
41
|
+
'max-duration' => saucelabs_max_duration_seconds.to_i,
|
42
|
+
'job-name' => ENV['SAUCELABS_JOB_NAME'] || Socket.gethostname
|
43
|
+
}.to_json
|
44
|
+
else
|
45
|
+
@configuration['selenium_browser_key']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def application_address
|
50
|
+
if start_tunnel? &&
|
51
|
+
[:sauceconnecttunnel, :saucetunnel].include?(@configuration['tunnel_method'].to_sym)
|
52
|
+
# We are using Sauce Labs and Sauce Connect Tunnel or Sauce Tunnel.
|
53
|
+
# We need to use a masquerade hostname on the EC2/Sauce end of the tunnel that will be unique within the scope of
|
54
|
+
# this account (e.g. pivotallabs). Therefore we mint a fairly unique hostname here.
|
55
|
+
hostname = Socket.gethostname.split(".").first
|
56
|
+
"#{hostname}-#{Process.pid}.com"
|
57
|
+
else
|
58
|
+
@configuration['application_address']
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# Takes a Webrat::Configuration object and configures it by calling methods on it
|
64
|
+
def configure_webrat(webrat_configuration_object)
|
65
|
+
# NOTE: application_port_for_selenium requires version > 0.7.3 of webrat
|
66
|
+
# Prior versions only have application_address, and don't have a concept of
|
67
|
+
# starting a rails server at one port, and hitting it at selenium via another
|
68
|
+
{
|
69
|
+
'selenium_server_address' => :selenium_server_address,
|
70
|
+
'selenium_server_port' => :selenium_server_port,
|
71
|
+
'selenium_browser_key' => :selenium_browser_key,
|
72
|
+
'application_address' => :application_address,
|
73
|
+
'application_port_for_selenium' => start_tunnel? ? :tunnel_to_localhost_port : :application_port,
|
74
|
+
'application_port' => :application_port
|
75
|
+
}.each do |webrat_configuration_method, our_accessor|
|
76
|
+
webrat_configuration_object.send("#{webrat_configuration_method}=", self.send(our_accessor).to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Takes a Polonium::Configuration object and configures it by calling methods on it
|
81
|
+
def configure_polonium(polonium_configuration_object)
|
82
|
+
{
|
83
|
+
'selenium_server_host' => :selenium_server_address,
|
84
|
+
'selenium_server_port' => :selenium_server_port,
|
85
|
+
'browser' => :selenium_browser_key,
|
86
|
+
'external_app_server_host' => :application_address,
|
87
|
+
'external_app_server_port' => :application_port
|
88
|
+
}.each do |polonium_configuration_method, our_accessor|
|
89
|
+
polonium_configuration_object.send("#{polonium_configuration_method}=", self.send(our_accessor).to_s)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_driver(selenium_args = {})
|
94
|
+
args = selenium_client_driver_args.merge(selenium_args)
|
95
|
+
say "Connecting to Selenium RC server at #{args[:host]}:#{args[:port]} (testing app at #{args[:url]})" if ENV['SAUCELABS_ADAPTER_DEBUG']
|
96
|
+
say "args = #{display_safely(args)}" if ENV['SAUCELABS_ADAPTER_DEBUG']
|
97
|
+
driver = ::Selenium::Client::Driver.new(args)
|
98
|
+
debug "done"
|
99
|
+
driver
|
100
|
+
end
|
101
|
+
|
102
|
+
def start_tunnel?
|
103
|
+
!tunnel_method.nil? && tunnel_method.to_sym != :othertunnel
|
104
|
+
end
|
105
|
+
|
106
|
+
def kill_mongrel_after_suite?
|
107
|
+
return true if kill_mongrel_after_suite.nil?
|
108
|
+
kill_mongrel_after_suite.to_s == 'true'
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.parse_yaml(selenium_yml_path)
|
112
|
+
raise "[saucelabs-adapter] could not open #{selenium_yml_path}" unless File.exist?(selenium_yml_path)
|
113
|
+
file_contents = File.open(selenium_yml_path).read
|
114
|
+
erb_parsed_file_contents = ERB.new(%{#{file_contents}}).result
|
115
|
+
configs = YAML.load(erb_parsed_file_contents)
|
116
|
+
@@selenium_configs ||= configs
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def display_safely(selenium_args)
|
122
|
+
safe = selenium_args.dup
|
123
|
+
begin
|
124
|
+
safe[:browser] = JSON.parse( safe[:browser])
|
125
|
+
safe[:browser]['access-key'] = safe[:browser]['access-key'][0..4] + '...'
|
126
|
+
safe[:browser] = safe[:browser].to_json
|
127
|
+
rescue
|
128
|
+
# args are not always json, e.g. when running locally
|
129
|
+
# for now, just ignore any exceptions when trying to parse args with json
|
130
|
+
end
|
131
|
+
safe.inspect
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_configuration(configuration_name)
|
135
|
+
@configuration = @@selenium_configs[configuration_name]
|
136
|
+
raise "[saucelabs-adapter] stanza '#{configuration_name}' not found in #{@selenium_yml}" unless @configuration
|
137
|
+
# If the Saucelabs-Adapter picked a port out of a range during this session, use it.
|
138
|
+
if ENV['SAUCELABS_ADAPTER_APPLICATION_PORT']
|
139
|
+
@configuration['application_port'] = ENV['SAUCELABS_ADAPTER_APPLICATION_PORT'].to_i
|
140
|
+
debug("Using application port #{application_port} from environment variable SAUCELABS_ADAPTER_APPLICATION_PORT", 2)
|
141
|
+
end
|
142
|
+
check_configuration(configuration_name)
|
143
|
+
end
|
144
|
+
|
145
|
+
def check_configuration(configuration_name)
|
146
|
+
errors = []
|
147
|
+
errors << require_attributes([:selenium_server_address, :selenium_server_port, :application_port])
|
148
|
+
if selenium_server_address == 'saucelabs.com'
|
149
|
+
errors << require_attributes([ :saucelabs_username, :saucelabs_access_key,
|
150
|
+
:saucelabs_browser_os, :saucelabs_browser, :saucelabs_browser_version,
|
151
|
+
:saucelabs_max_duration_seconds ],
|
152
|
+
:when => "when selenium_server_address is saucelabs.com")
|
153
|
+
if tunnel_method
|
154
|
+
case tunnel_method.to_sym
|
155
|
+
when nil, ""
|
156
|
+
when :saucetunnel
|
157
|
+
errors << require_attributes([:tunnel_to_localhost_port ], :when => "if tunnel_method is :saucetunnel")
|
158
|
+
when :sauceconnecttunnel
|
159
|
+
errors << require_attributes([:tunnel_to_localhost_port ], :when => "if tunnel_method is :sauceconnecttunnel")
|
160
|
+
when :othertunnel
|
161
|
+
errors << require_attributes([:application_address], :when => "when tunnel_method is :othertunnel")
|
162
|
+
errors << require_attributes([:tunnel_to_localhost_port ], :when => "if tunnel_method is :othertunnel")
|
163
|
+
when :sshtunnel
|
164
|
+
errors << require_attributes([:application_address], :when => "when tunnel_method is :sshtunnel")
|
165
|
+
errors << require_attributes([:tunnel_password, :tunnel_keyfile],
|
166
|
+
:when => "when tunnel_method is :sshtunnel",
|
167
|
+
:any_or_all => :any)
|
168
|
+
if application_address && application_port.is_a?(String) && application_port =~ /(\d+)-(\d+)/
|
169
|
+
# We have been given a port range. Find an unused one.
|
170
|
+
port = find_unused_port(application_address, ($1.to_i)..($2.to_i))
|
171
|
+
@configuration['application_port'] = port
|
172
|
+
@configuration['tunnel_to_localhost_port'] = port if test_framework.to_sym == :webrat
|
173
|
+
# Pass this calculated value on to any other instances of SeleniumConfig created
|
174
|
+
ENV['SAUCELABS_ADAPTER_APPLICATION_PORT'] = port.to_s
|
175
|
+
end
|
176
|
+
if tunnel_keyfile && !File.exist?(File.expand_path(tunnel_keyfile))
|
177
|
+
errors << "tunnel_keyfile '#{tunnel_keyfile}' does not exist"
|
178
|
+
end
|
179
|
+
else
|
180
|
+
errors << "Unknown tunnel_method: #{tunnel_method}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
else
|
184
|
+
errors << require_attributes([:selenium_browser_key, :application_address ],
|
185
|
+
:when => "unless server is saucelabs.com")
|
186
|
+
end
|
187
|
+
|
188
|
+
errors.flatten!.compact!
|
189
|
+
if !errors.empty?
|
190
|
+
raise "[saucelabs-adapter] Aborting; stanza #{configuration_name} has the following errors:\n\t" + errors.join("\n\t")
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def require_attributes(names, options = {})
|
195
|
+
default_options = {
|
196
|
+
:when => "",
|
197
|
+
:any_or_all => :all
|
198
|
+
}
|
199
|
+
options = default_options.merge(options)
|
200
|
+
|
201
|
+
errors = []
|
202
|
+
names.each do |attribute|
|
203
|
+
errors << "#{attribute} is required #{options[:when]}" if send(attribute).nil?
|
204
|
+
end
|
205
|
+
errors = [] if options[:any_or_all] == :any && errors.size < names.size
|
206
|
+
errors
|
207
|
+
end
|
208
|
+
|
209
|
+
def selenium_client_driver_args
|
210
|
+
{
|
211
|
+
:host => selenium_server_address,
|
212
|
+
:port => selenium_server_port.to_s,
|
213
|
+
:browser => selenium_browser_key,
|
214
|
+
:url => "http://#{application_address}:#{application_port}",
|
215
|
+
:timeout_in_seconds => 600
|
216
|
+
}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Sauce
|
2
|
+
module JSUnit
|
3
|
+
module Utilities
|
4
|
+
|
5
|
+
def diagnostics_prefix
|
6
|
+
@diagnostics_prefix ||= '[saucelabs-adapter]'
|
7
|
+
end
|
8
|
+
|
9
|
+
def say(what)
|
10
|
+
STDOUT.puts "#{diagnostics_prefix} #{what}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def debug(what, print_if_level_ge = 0)
|
14
|
+
if ENV['SAUCELABS_ADAPTER_DEBUG']
|
15
|
+
actual_level = ENV['SAUCELABS_ADAPTER_DEBUG'].to_i
|
16
|
+
STDOUT.puts "#{diagnostics_prefix} #{what}" if print_if_level_ge >= actual_level
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def raise_with_message(message)
|
21
|
+
raise "#{diagnostics_prefix} #{message}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_unused_port(hostname, range = (3000..5000))
|
25
|
+
debug 'searching for unused port', 2
|
26
|
+
range.each do |port|
|
27
|
+
debug "trying #{hostname}:#{port}", 2
|
28
|
+
begin
|
29
|
+
socket = TCPSocket.new(hostname, port)
|
30
|
+
rescue Errno::ECONNREFUSED
|
31
|
+
debug "it's good, returning #{port}", 2
|
32
|
+
return port
|
33
|
+
ensure
|
34
|
+
socket.close if socket
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# parameters required when invoked by test_unit
|
40
|
+
def start_mongrel(suite_name = {})
|
41
|
+
pid_file = File.join(RAILS_ROOT, "tmp", "pids", "mongrel_selenium.pid")
|
42
|
+
port = suite_name[:port] rescue @selenium_config.application_port
|
43
|
+
say "Starting mongrel at #{pid_file}, port #{port}"
|
44
|
+
system "mongrel_rails start -d --chdir='#{RAILS_ROOT}' --port=#{port} --environment=test --pid #{pid_file} %"
|
45
|
+
end
|
46
|
+
|
47
|
+
def kill_mongrel_if_needed(suite_name = {})
|
48
|
+
mongrel_pid_file = File.join(RAILS_ROOT, "tmp", "pids", "mongrel_selenium.pid")
|
49
|
+
if File.exists?(mongrel_pid_file)
|
50
|
+
pid = File.read(mongrel_pid_file).to_i
|
51
|
+
say "Killing mongrel at #{pid}"
|
52
|
+
Process.kill("KILL", pid)
|
53
|
+
end
|
54
|
+
if File.exists?(mongrel_pid_file)
|
55
|
+
FileUtils.rm(mongrel_pid_file)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup_tunnel(suite_name = {})
|
60
|
+
@tunnel = SaucelabsAdapter::Tunnel.factory(@selenium_config)
|
61
|
+
@tunnel.start_tunnel
|
62
|
+
end
|
63
|
+
|
64
|
+
def teardown_tunnel(suite_name = {})
|
65
|
+
@tunnel.shutdown
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'sauce/jsunit/utilities'
|
2
|
+
module Sauce
|
3
|
+
module JSUnit
|
4
|
+
include Utilities
|
5
|
+
|
6
|
+
def requires
|
7
|
+
require 'sauce'
|
8
|
+
require 'sauce/jsunit/run_utils'
|
9
|
+
require 'sauce/jsunit/selenium_config'
|
10
|
+
require "selenium/client"
|
11
|
+
require 'lsof'
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup_jsunit_selenium(options = {})
|
15
|
+
@diagnostics_prefix = '[SauceJSUnit]'
|
16
|
+
requires
|
17
|
+
@selenium_config = SeleniumConfig.new(ENV['SELENIUM_ENV'])
|
18
|
+
start_app_server(options)
|
19
|
+
@tunnel = Sauce::Connect.new(:port => 8080, :domain => "jsunit.test")
|
20
|
+
@tunnel.wait_until_ready
|
21
|
+
@selenium_driver = Sauce::Selenium.new(:job_name => "JSUnit", :browser_url=>"http://jsunit.test/")
|
22
|
+
debug "calling @selenium_driver.start"
|
23
|
+
@selenium_driver.start_new_browser_session :trustAllSSLCertificates => false
|
24
|
+
debug "@selenium_driver.start done"
|
25
|
+
end
|
26
|
+
|
27
|
+
def teardown_jsunit_selenium
|
28
|
+
@selenium_driver.stop
|
29
|
+
@tunnel.disconnect
|
30
|
+
stop_app_server
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_jsunit_test(jsunit_params, options = {})
|
34
|
+
if $:.detect{ |x| x =~ /Selenium/}
|
35
|
+
raise_with_message 'Selenium gem should not be in path! (deprecated in favor of selenium-client, which we require)'
|
36
|
+
end
|
37
|
+
|
38
|
+
default_jsunit_params = {
|
39
|
+
:testPage => "/jsunit/javascripts/test-pages/suite.html",
|
40
|
+
:autorun => "true",
|
41
|
+
:setupPageTimeout => "60",
|
42
|
+
:pageLoadTimeout => "60",
|
43
|
+
:suppressCacheBuster => (@selenium_config.selenium_server_address == 'saucelabs.com').to_s
|
44
|
+
}
|
45
|
+
jsunit_params = default_jsunit_params.merge(jsunit_params)
|
46
|
+
|
47
|
+
test_url = "/jsunit/javascripts/jsunit/jsunit/testRunner.html?" + jsunit_params.map { |k,v| "#{k}=#{v}" }.join("&")
|
48
|
+
if @selenium_config.jsunit_polling_interval_seconds
|
49
|
+
options = {:polling_interval => @selenium_config.jsunit_polling_interval_seconds}.merge(options)
|
50
|
+
end
|
51
|
+
run_suite(@selenium_driver, test_url, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def pid_file
|
57
|
+
prepare_pid_file("#{RAILS_ROOT}/tmp/pids", "mongrel_selenium.pid")
|
58
|
+
end
|
59
|
+
|
60
|
+
def prepare_pid_file(file_path, pid_file_name)
|
61
|
+
FileUtils.mkdir_p File.expand_path(file_path)
|
62
|
+
File.expand_path("#{file_path}/#{pid_file_name}")
|
63
|
+
end
|
64
|
+
|
65
|
+
def local_app_server_port
|
66
|
+
@selenium_config.tunnel_to_localhost_port || @selenium_config.application_port
|
67
|
+
end
|
68
|
+
|
69
|
+
def start_app_server(options = {})
|
70
|
+
stop_app_server
|
71
|
+
say "starting application server:"
|
72
|
+
app_server_logfile_path = options[:app_server_logfile_path] || "#{RAILS_ROOT}/log/jsunit_jetty_app_server.log"
|
73
|
+
RunUtils.run "ant -f #{RAILS_ROOT}/public/javascripts/jsunit/jsunit/build.xml start_server " +
|
74
|
+
"-Dport=#{local_app_server_port} " +
|
75
|
+
"-DcustomJsUnitJarPath=#{RAILS_ROOT}/public/javascripts/jsunit/jsunit_jar/jsunit.jar " +
|
76
|
+
"-DresourceBase=#{RAILS_ROOT}/public >> #{app_server_logfile_path} 2>&1 &"
|
77
|
+
end
|
78
|
+
|
79
|
+
def stop_app_server
|
80
|
+
raise_with_message "oops don't know port app server is running on" unless local_app_server_port
|
81
|
+
while Lsof.running?(local_app_server_port)
|
82
|
+
say "Killing app server at #{local_app_server_port}..."
|
83
|
+
Lsof.kill(local_app_server_port)
|
84
|
+
sleep 1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_suite(selenium_driver, suite_path, options = {})
|
89
|
+
default_options = {
|
90
|
+
:timeout_in_seconds => 1200,
|
91
|
+
:polling_interval => 5
|
92
|
+
}
|
93
|
+
options = default_options.merge(options)
|
94
|
+
|
95
|
+
selenium_driver.open(suite_path)
|
96
|
+
|
97
|
+
# It would be nice if this worked, but it doesn't (it returns nil even though 'Done' is not in the element).
|
98
|
+
# selenium.wait_for_condition(
|
99
|
+
# "new RegExp('Done').test(window.mainFrame.mainStatus.document.getElementById('content').innerHTML)")
|
100
|
+
|
101
|
+
tests_completed = false
|
102
|
+
begin_time = Time.now
|
103
|
+
status = ""
|
104
|
+
say "Starting to poll JsUnit (every #{options[:polling_interval]}s)..." if options[:verbose]
|
105
|
+
while (Time.now - begin_time) < options[:jsunit_suite_timeout_seconds] && !tests_completed
|
106
|
+
sleep options[:polling_interval]
|
107
|
+
debug "polling now...", 2
|
108
|
+
status = selenium_driver.js_eval("window.mainFrame.mainStatus.document.getElementById('content').innerHTML")
|
109
|
+
status.gsub!(/^<[bB]>Status:<\/[bB]> /, '')
|
110
|
+
# Long form: window.frames['mainFrame'].frames['mainCounts'].frames['mainCountsRuns'].document.getElementById('content').innerHTML
|
111
|
+
runs = selenium_driver.js_eval("window.mainFrame.mainCounts.mainCountsRuns.document.getElementById('content').innerHTML").strip
|
112
|
+
fails = selenium_driver.js_eval("window.mainFrame.mainCounts.mainCountsFailures.document.getElementById('content').innerHTML").strip
|
113
|
+
errors = selenium_driver.js_eval("window.mainFrame.mainCounts.mainCountsErrors.document.getElementById('content').innerHTML").strip
|
114
|
+
run_count = runs.match(/\d+$/)[0].to_i
|
115
|
+
fail_count = fails.match(/\d+$/)[0].to_i
|
116
|
+
error_count = errors.match(/\d+$/)[0].to_i
|
117
|
+
say "runs/fails/errors: #{run_count}/#{fail_count}/#{error_count} status: #{status}" if options[:verbose]
|
118
|
+
if status =~ /^Done /
|
119
|
+
tests_completed = true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
raise_with_message "Tests failed to complete after #{options[:jsunit_suite_timeout_seconds]}, status was '#{status}'" unless tests_completed
|
123
|
+
|
124
|
+
say "********** JSUnit tests complete, Runs: #{run_count}, Fails: #{fail_count}, Errors: #{error_count} **********"
|
125
|
+
|
126
|
+
if (fail_count + error_count > 0)
|
127
|
+
error_messages = selenium_driver.js_eval("window.mainFrame.mainErrors.document.getElementsByName('problemsList')[0].innerHTML")
|
128
|
+
say "Error messages: #{error_messages}"
|
129
|
+
end
|
130
|
+
|
131
|
+
(fail_count + error_count) == 0
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsunit-sauce
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Eric Allen
|
14
|
+
- Chad Wooley
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-02-04 00:00:00 -08:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: sauce
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 95
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
- 16
|
34
|
+
- 0
|
35
|
+
version: 0.16.0
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: childprocess
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 23
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
- 1
|
50
|
+
- 6
|
51
|
+
version: 0.1.6
|
52
|
+
type: :runtime
|
53
|
+
version_requirements: *id002
|
54
|
+
description: Adapter to run JsUnit test suites using browsers in Sauce OnDemand
|
55
|
+
email: eric@hackerengineer.net
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files:
|
61
|
+
- LICENSE
|
62
|
+
- README.md
|
63
|
+
files:
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- /Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit/run_utils.rb
|
68
|
+
- /Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit/selenium_config.rb
|
69
|
+
- /Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit/utilities.rb
|
70
|
+
- /Users/epall/Dropbox/code/jsunit-sauce/lib/sauce/jsunit.rb
|
71
|
+
- /Users/epall/Dropbox/code/jsunit-sauce/test/helper.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/saucelabs/jsunit-sauce
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.4.2
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: JsUnit + Sauce OnDemand
|
106
|
+
test_files: []
|
107
|
+
|