sauce_ruby 3.5.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.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/bin/sauce +103 -0
  3. data/lib/childprocess/process.rb +34 -0
  4. data/lib/generators/sauce/install/install_generator.rb +54 -0
  5. data/lib/parallel_tests/cli_patch.rb +23 -0
  6. data/lib/parallel_tests/saucecucumber/runner.rb +43 -0
  7. data/lib/parallel_tests/saucerspec/runner.rb +40 -0
  8. data/lib/sauce.rb +33 -0
  9. data/lib/sauce/capybara.rb +189 -0
  10. data/lib/sauce/client.rb +40 -0
  11. data/lib/sauce/config.rb +434 -0
  12. data/lib/sauce/driver_pool.rb +5 -0
  13. data/lib/sauce/heroku.rb +18 -0
  14. data/lib/sauce/job.rb +159 -0
  15. data/lib/sauce/parallel.rb +16 -0
  16. data/lib/sauce/parallel/test_broker.rb +110 -0
  17. data/lib/sauce/parallel/test_group.rb +24 -0
  18. data/lib/sauce/railtie.rb +11 -0
  19. data/lib/sauce/raketasks.rb +83 -0
  20. data/lib/sauce/rspec/rspec.rb +186 -0
  21. data/lib/sauce/rspec/rspec_formatter.rb +20 -0
  22. data/lib/sauce/rspec/rspec_one_support.rb +66 -0
  23. data/lib/sauce/selenium.rb +95 -0
  24. data/lib/sauce/test_base.rb +21 -0
  25. data/lib/sauce/test_unit.rb +111 -0
  26. data/lib/sauce/utilities.rb +80 -0
  27. data/lib/sauce/utilities/connect.rb +45 -0
  28. data/lib/sauce/utilities/rails_server.rb +95 -0
  29. data/lib/sauce/utilities/rake.rb +32 -0
  30. data/lib/sauce/version.rb +9 -0
  31. data/lib/sauce/webmock.rb +16 -0
  32. data/lib/tasks/parallel_testing.rb +148 -0
  33. data/spec/cucumber_helper.rb +42 -0
  34. data/spec/integration/connect/spec/spec_helper.rb +21 -0
  35. data/spec/integration/connect/spec/start_tunnel_spec.rb +9 -0
  36. data/spec/integration/connect/spec/with_capybara_spec.rb +10 -0
  37. data/spec/integration/connect_integration_spec.rb +99 -0
  38. data/spec/integration/rspec-capybara/spec/capybara_required_last_spec.rb +18 -0
  39. data/spec/integration/rspec-capybara/spec/integration_spec.rb +17 -0
  40. data/spec/integration/rspec-capybara/spec/sauce_config_spec.rb +13 -0
  41. data/spec/integration/rspec-capybara/spec/sauce_required_last_spec.rb +21 -0
  42. data/spec/integration/rspec-capybara/spec/selenium/selenium_with_capybara.rb +12 -0
  43. data/spec/integration/rspec-capybara/spec/spec_helper.rb +37 -0
  44. data/spec/integration/rspec/spec/integration_spec.rb +13 -0
  45. data/spec/integration/rspec/spec/selenium/selenium_directory_spec.rb +20 -0
  46. data/spec/integration/rspec/spec/spec_helper.rb +52 -0
  47. data/spec/integration/rspec/spec/tagging/selenium_tagging_spec.rb +29 -0
  48. data/spec/integration/testunit/test/capybara_integration_test.rb +37 -0
  49. data/spec/integration/testunit/test/integration_test.rb +21 -0
  50. data/spec/integration/testunit/test/test_helper.rb +13 -0
  51. data/spec/parallel_tests/sauce_rspec_runner_spec.rb +23 -0
  52. data/spec/sauce/capybara_spec.rb +386 -0
  53. data/spec/sauce/config/browser_spec.rb +73 -0
  54. data/spec/sauce/config/config_spec.rb +400 -0
  55. data/spec/sauce/config/default_browsers_spec.rb +46 -0
  56. data/spec/sauce/config/environment_config_spec.rb +106 -0
  57. data/spec/sauce/config/load_defaults_spec.rb +50 -0
  58. data/spec/sauce/config/perfile_browser_spec.rb +105 -0
  59. data/spec/sauce/connect_spec.rb +21 -0
  60. data/spec/sauce/cucumber_spec.rb +212 -0
  61. data/spec/sauce/driver_pool_spec.rb +8 -0
  62. data/spec/sauce/file_detector_spec.rb +15 -0
  63. data/spec/sauce/jasmine_spec.rb +30 -0
  64. data/spec/sauce/parallel/test_broker_spec.rb +77 -0
  65. data/spec/sauce/selenium_spec.rb +60 -0
  66. data/spec/sauce/tasks_spec.rb +180 -0
  67. data/spec/sauce/utilities/rails_server_spec.rb +109 -0
  68. data/spec/sauce/utilities/rake_spec.rb +46 -0
  69. data/spec/sauce/utilities/utilities_spec.rb +137 -0
  70. data/spec/sauce_helper.rb +8 -0
  71. data/spec/spec_helper.rb +27 -0
  72. metadata +390 -0
@@ -0,0 +1,40 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+
4
+ module Sauce
5
+ # The module that brokers most communication with Sauce Labs' REST API
6
+ class Client
7
+ class BadAccessError < StandardError; end #:nodoc
8
+ class MisconfiguredError < StandardError; end #:nodoc
9
+
10
+ attr_accessor :client
11
+ attr_accessor :protocol, :host, :port, :api_path, :api_version, :ip, :api_url
12
+ attr_accessor :jobs
13
+
14
+ def initialize(options={})
15
+ config = Sauce::Config.new
16
+
17
+ @protocol = options[:protocol] || "http"
18
+ @host = options[:host] || "saucelabs.com"
19
+ @port = options[:port] || 80
20
+ @api_path = options[:api_path] || "rest"
21
+ @api_version= options[:api_version] || 1
22
+
23
+ raise MisconfiguredError if config.username.nil? or config.access_key.nil?
24
+ @api_url = "#{@protocol}://#{config.username}:#{config.access_key}@#{@host}:#{@port}/#{@api_path}/v#{@api_version}/#{config.username}/"
25
+ @client = RestClient::Resource.new @api_url
26
+
27
+ @jobs = Sauce::Job
28
+ @jobs.client = @client
29
+ @jobs.account = {
30
+ :username => config.username,
31
+ :access_key => config.access_key,
32
+ :ip => @ip
33
+ }
34
+ end
35
+
36
+ def [](url)
37
+ @client[url]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,434 @@
1
+ require 'json'
2
+ require 'yaml'
3
+ require 'uri'
4
+
5
+ module Sauce
6
+ def self.config
7
+ yield get_config
8
+ end
9
+
10
+ def self.get_config(default = false)
11
+ get_default = default == :default ? {} : false
12
+ @cfg ||= Sauce::Config.new(get_default)
13
+ end
14
+
15
+ def self.clear_config
16
+ @cfg = nil
17
+ end
18
+
19
+ class Config
20
+ attr_reader :opts
21
+
22
+ DEFAULT_OPTIONS = {
23
+ :host => "ondemand.saucelabs.com",
24
+ :port => 80,
25
+ :browser_url => "http://saucelabs.com",
26
+ :job_name => "Unnamed Ruby job",
27
+ :start_tunnel => true,
28
+ :start_local_application => true,
29
+ :warn_on_skipped_integration => true,
30
+ :skip_connection_test => false
31
+ }
32
+
33
+ DEFAULT_BROWSERS = {
34
+ :browsers => [
35
+ ["Windows 8", "Internet Explorer", "10"],
36
+ ["Windows 7", "Firefox", "20"],
37
+ ["OS X 10.8", "Safari", "6"],
38
+ ["Linux", "Chrome", nil]
39
+ ]
40
+ }
41
+
42
+ POTENTIAL_PORTS = [
43
+ 3000, 3001, 3030, 3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503, 5000,
44
+ 5001, 5050, 5555, 5432, 6000, 6001, 6060, 6666, 6543, 7000, 7070, 7774,
45
+ 7777, 8000, 8001, 8003, 8031, 8080, 8081, 8765, 8888, 9000, 9001, 9080,
46
+ 9090, 9876, 9999, 49221, 55001, 80, 443, 888, 2000, 2001, 2020, 2109,
47
+ 2222, 2310
48
+ ]
49
+
50
+ ENVIRONMENT_VARIABLES = %w{SAUCE_HOST SAUCE_PORT SAUCE_BROWSER_URL SAUCE_USERNAME
51
+ SAUCE_ACCESS_KEY SAUCE_OS SAUCE_BROWSER SAUCE_BROWSER_VERSION SAUCE_JOB_NAME
52
+ SAUCE_FIREFOX_PROFILE_URL SAUCE_USER_EXTENSIONS_URL
53
+ SAUCE_ONDEMAND_BROWSERS SAUCE_USER_NAME SAUCE_API_KEY}
54
+
55
+ PLATFORMS = {
56
+ "Windows 2003" => "WINDOWS",
57
+ "Windows 2008" => "VISTA",
58
+ "Linux" => "LINUX"
59
+ }
60
+
61
+ BROWSERS = {
62
+ "iexplore" => "internet explorer",
63
+ "ie" => "internet explorer"
64
+ }
65
+
66
+ SAUCE_OPTIONS = %w{record-video record-screenshots capture-html tags
67
+ sauce-advisor single-window user-extensions-url firefox-profile-url
68
+ max-duration idle-timeout build custom-data tunnel-identifier
69
+ selenium-version command-timeout prerun prerun-args screen-resolution
70
+ disable-popup-handler avoid-proxy public name iedriver-version parent-tunnel}
71
+
72
+ def self.get_application_port
73
+ port_index = ENV["TEST_ENV_NUMBER"].to_i
74
+ return POTENTIAL_PORTS[port_index]
75
+ end
76
+
77
+ def self.called_from_integrations?
78
+ @called_from_integrations || false
79
+ end
80
+
81
+ def self.called_from_integrations
82
+ @called_from_integrations = true
83
+ end
84
+
85
+ # Creates a new instance of Sauce::Config
86
+ #
87
+ # @param [Hash, Boolean] opts Any value you'd set with [:option], as a hash. If false, skip loading default options
88
+ # @option opts [Boolean] :without_defaults Set true to skip loading default values
89
+ #
90
+ # @return [Sauce::Config]
91
+ def initialize(opts={})
92
+ @opts = {}
93
+ @undefaulted_opts = {}
94
+ if opts != false
95
+ if (!opts[:without_defaults])
96
+ @opts.merge! DEFAULT_OPTIONS
97
+ @opts.merge! DEFAULT_BROWSERS
98
+ @opts.merge!({:application_port => Sauce::Config.get_application_port})
99
+
100
+ @undefaulted_opts.merge! load_options_from_yaml
101
+ @undefaulted_opts.merge! load_options_from_environment
102
+ @undefaulted_opts.merge! load_options_from_heroku unless ENV["SAUCE_DISABLE_HEROKU_CONFIG"]
103
+
104
+ global_config = Sauce.get_config
105
+ @undefaulted_opts.merge! global_config.opts if global_config.opts
106
+ @whitelisted_capabilities = global_config.whitelisted_capabilities
107
+ end
108
+
109
+ @undefaulted_opts.merge! opts
110
+ @opts.merge! @undefaulted_opts
111
+ end
112
+ end
113
+
114
+ def [](key)
115
+ @opts[key]
116
+ end
117
+
118
+ def []=(key, value)
119
+ if(key == :browsers)
120
+ value = [value] unless value.first.instance_of?(Array)
121
+ end
122
+ @undefaulted_opts.merge!({key => value})
123
+ @opts[key] = value
124
+ end
125
+
126
+ def has_key?(key)
127
+ @opts.has_key? key
128
+ end
129
+
130
+ def silence_warnings
131
+ false
132
+ end
133
+
134
+ def method_missing(meth, *args)
135
+ unless self.silence_warnings
136
+ warn "[DEPRECATED] This method (#{meth}) is deprecated, please use the [] and []= accessors instead"
137
+ end
138
+ if meth.to_s =~ /(.*)=$/
139
+ self[$1.to_sym] = args[0]
140
+ return args[0]
141
+ elsif meth.to_s =~ /(.*)\?$/
142
+ return self[$1.to_sym]
143
+ else
144
+ return self[meth]
145
+ end
146
+ end
147
+
148
+ def whitelisted_capabilities
149
+ @whitelisted_capabilities ||= Set.new
150
+ end
151
+
152
+ def whitelist capability
153
+ cap = capability.to_s
154
+ wl = whitelisted_capabilities || Set.new
155
+ @whitelisted_capabilities = wl.add cap
156
+ end
157
+
158
+ def to_browser_string
159
+ browser_options = {
160
+ 'username' => @opts[:username],
161
+ 'access-key' => @opts[:access_key],
162
+ 'os' => os,
163
+ 'browser' => browser,
164
+ 'browser-version' => browser_version,
165
+ 'name' => @opts[:name] || @opts[:job_name]}
166
+
167
+ SAUCE_OPTIONS.each do |opt|
168
+ [opt, opt.gsub("-", "_")].map(&:to_sym).each do |sym|
169
+ browser_options[opt] = @opts[sym] if @opts.include? sym
170
+ end
171
+ end
172
+ return browser_options.to_json
173
+ end
174
+
175
+ def to_desired_capabilities
176
+ desired_capabilities = {
177
+ :browserName => BROWSERS[browser] || browser,
178
+ :version => browser_version,
179
+ :platform => PLATFORMS[os] || os,
180
+ :name =>@opts[:job_name],
181
+ :client_version => client_version
182
+ }
183
+
184
+ allowed_options = whitelisted_capabilities + SAUCE_OPTIONS
185
+
186
+ allowed_options.each do |opt|
187
+ [opt, opt.gsub("-", "_")].map(&:to_sym).each do |sym|
188
+ if @opts.include? sym
189
+ desired_capabilities[opt.to_sym] = @opts[sym]
190
+ elsif @opts.include? sym.to_s
191
+ desired_capabilities[opt.to_sym] = @opts[sym.to_s]
192
+ end
193
+ end
194
+ end
195
+
196
+ desired_capabilities
197
+ end
198
+
199
+ def browsers
200
+ if @undefaulted_opts[:browser]
201
+ # If a specific browser was requested, ignore :browsers and
202
+ # use that one. This allows a setup with :browsers to launch
203
+ # sub-processes pointed just at each browser in the list.
204
+ return [[os, browser, browser_version]]
205
+ end
206
+
207
+ return @opts[:browsers] if @opts.include? :browsers
208
+ return [[os, browser, browser_version]]
209
+ end
210
+
211
+ def caps_for_location(file, linenumber=nil)
212
+ Sauce::Config.called_from_integrations
213
+ perfile_browsers = @opts[:perfile_browsers]
214
+
215
+ if perfile_browsers
216
+ platforms = []
217
+ test_location = "#{file}:#{linenumber}"
218
+ if linenumber && (perfile_browsers.include? test_location)
219
+ platforms = perfile_browsers[test_location]
220
+ else
221
+ platforms = perfile_browsers[file]
222
+ end
223
+ platforms.map { |p| [p['os'], p['browser'], p['version'], (p['caps'] || {})] }
224
+ else
225
+ browsers
226
+ end
227
+ end
228
+
229
+ def browser
230
+ if single_browser_set?
231
+ return @undefaulted_opts[:browser]
232
+ end
233
+ if !ENV["TEST_ENV_NUMBER"] && @opts[:browsers]
234
+ @opts[:browsers][0][1]
235
+ else
236
+ raise StandardError, no_browser_message
237
+ end
238
+ end
239
+
240
+ def os
241
+ if single_browser_set?
242
+ return @undefaulted_opts[:os]
243
+ end
244
+ if !ENV["TEST_ENV_NUMBER"] && @opts[:browsers]
245
+ @opts[:browsers][0][0]
246
+ else
247
+ @opts[:os]
248
+ end
249
+ end
250
+
251
+ def browser_version
252
+ if single_browser_set?
253
+ return @undefaulted_opts[:browser_version] || @undefaulted_opts[:version]
254
+ end
255
+ if !ENV["TEST_ENV_NUMBER"] && @opts[:browsers]
256
+ @opts[:browsers][0][2]
257
+ else
258
+ @opts[:browser_version]
259
+ end
260
+ end
261
+
262
+ def domain
263
+ return @opts[:domain] if @opts.include? :domain
264
+ return URI.parse(@opts[:browser_url]).host
265
+ end
266
+
267
+ def local?
268
+ return ENV['LOCAL_SELENIUM']
269
+ end
270
+
271
+ def username
272
+ @opts[:username]
273
+ end
274
+
275
+ def access_key
276
+ @opts[:access_key]
277
+ end
278
+
279
+ def host
280
+ @opts[:host]
281
+ end
282
+
283
+ def port
284
+ @opts[:port]
285
+ end
286
+
287
+ def after_job(hook, &block)
288
+ hooks = @opts[:after_job_hooks] || {}
289
+ hooks[hook] = block unless hooks[hook]
290
+ @opts[:after_job_hooks] = hooks
291
+ end
292
+
293
+ def run_post_job_hooks(job_id, platform, job_name, job_success)
294
+ @opts[:after_job_hooks].each do |key, hook|
295
+ hook.call job_id, platform, job_name, job_success
296
+ end
297
+ end
298
+
299
+ def tools
300
+ tools = []
301
+ tools << "Rspec" if is_defined? "RSpec"
302
+ tools << "Capybara" if is_defined? "Capybara"
303
+ tools << "Cucumber" if is_defined? "Cucumber"
304
+ tools << "Test::Unit" if is_defined?("Test", "Unit")
305
+ tools
306
+ end
307
+
308
+ # Only here to be stubbed for testing. Gross.
309
+ def is_defined? (top_mod, sub_mod = nil)
310
+ return_value = Object.const_defined? top_mod
311
+ unless !return_value || sub_mod.nil?
312
+ return_value = Object.const_get(top_mod).const_defined? sub_mod
313
+ end
314
+
315
+ return_value
316
+ end
317
+
318
+ private
319
+
320
+ def client_version
321
+ "Ruby: #{RUBY_ENGINE} #{RUBY_VERSION} (#{RUBY_PLATFORM}) Sauce gem: #{Sauce.version} Tools: #{tools.to_s}"
322
+ end
323
+
324
+ def load_options_from_environment
325
+ return extract_options_from_hash(ENV)
326
+ end
327
+
328
+ # Heroku supports multiple apps per $PWD. Specify $SAUCE_HEROKU_APP if
329
+ # needed otherwise this can still time out.
330
+ def load_options_from_heroku
331
+ @@heroku_environment ||= begin
332
+ if File.exists?(File.expand_path('~/.heroku'))
333
+ heroku_app = ENV['SAUCE_HEROKU_APP']
334
+ cmd = "heroku config #{heroku_app ? "--app #{heroku_app}": ''}"
335
+ cmd += "--shell 2>/dev/null"
336
+ buffer = IO.popen(cmd) { |out| out.read }
337
+ if $?.exitstatus == 0
338
+ env = {}
339
+ buffer.split("\n").each do |line|
340
+ key, value = line.split("=")
341
+ env[key] = value
342
+ end
343
+ extract_options_from_hash(env)
344
+ else
345
+ {}
346
+ end
347
+ else
348
+ {}
349
+ end
350
+ rescue Errno::ENOENT
351
+ {} # not a Heroku environment
352
+ end
353
+ return @@heroku_environment
354
+ end
355
+
356
+ def load_options_from_yaml
357
+ paths = [
358
+ "ondemand.yml",
359
+ File.join("config", "ondemand.yml"),
360
+ File.expand_path("../../../ondemand.yml", __FILE__),
361
+ File.join(File.expand_path("~"), ".sauce", "ondemand.yml")
362
+ ]
363
+
364
+ paths.each do |path|
365
+ if File.exist? path
366
+ conf = YAML.load_file(path)
367
+ return conf.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
368
+ end
369
+ end
370
+ return {}
371
+ end
372
+
373
+ def extract_options_from_hash(env)
374
+ hash = Hash[env]
375
+ opts = {}
376
+
377
+ on_demand = hash.delete "SAUCE_ONDEMAND_BROWSERS"
378
+ env_browsers = hash.delete "SAUCE_BROWSERS"
379
+ username = hash.delete("SAUCE_USERNAME") || hash.delete("SAUCE_USER_NAME")
380
+ access_key = hash.delete("SAUCE_ACCESS_KEY") || hash.delete("SAUCE_API_KEY")
381
+
382
+ hash.select {|k,v| k.start_with? "SAUCE_"}.each do |k,v|
383
+ opts[k.downcase.sub("sauce_", "").to_sym] = v
384
+ end
385
+
386
+ opts[:job_name] = hash['SAUCE_JOB_NAME'] || hash['JOB_NAME']
387
+ opts[:build] = (hash['BUILD_TAG'] ||
388
+ hash['BUILD_NUMBER'] ||
389
+ hash['TRAVIS_BUILD_NUMBER'] ||
390
+ hash['CIRCLE_BUILD_NUM'])
391
+
392
+ if hash.include? 'URL'
393
+ opts['SAUCE_BROWSER_URL'] = "http://#{hash['URL']}/"
394
+ end
395
+
396
+ if on_demand
397
+ browsers = JSON.parse(on_demand)
398
+ opts[:browsers] = browsers.map { |x| [x['os'], x['browser'], x['browser-version']] }
399
+ end
400
+
401
+ if env_browsers
402
+ browsers = JSON.parse(env_browsers)
403
+ opts[:browsers] = browsers.map { |x| [x['os'], x['browser'], x['version'], x['caps']] }
404
+ end
405
+
406
+ if hash.include? 'SAUCE_PERFILE_BROWSERS'
407
+ opts[:perfile_browsers] = JSON.parse(hash['SAUCE_PERFILE_BROWSERS'])
408
+ end
409
+
410
+ opts[:username] = username if username
411
+ opts[:access_key] = access_key if access_key
412
+
413
+ return opts.delete_if {|key, value| value.nil?}
414
+ end
415
+
416
+ private
417
+
418
+ def single_browser_set?
419
+ @undefaulted_opts[:browser] || @undefaulted_opts[:os] || @undefaulted_opts[:version]
420
+ end
421
+
422
+ def no_browser_message
423
+ <<-MESSAGE
424
+ No browser has been configured.
425
+
426
+ It seems you're trying to run your tests in parallel, but haven't configured your specs/tests to use the Sauce integration.
427
+
428
+ To fix this, add :sauce => true to your specs or make your tests subclasses of Sauce::TestCase or Sauce::RailsTestCase.
429
+
430
+ For more details check the gem readme at https://github.com/saucelabs/sauce_ruby/blob/master/README.markdown
431
+ MESSAGE
432
+ end
433
+ end
434
+ end