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