sauce_ruby 3.5.6

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d0e0aad8cbfd23b54304b3b5190e3af87159e509
4
+ data.tar.gz: 35a9bc2da5f12d59c72f17a06836d9c57d3310fa
5
+ SHA512:
6
+ metadata.gz: c27dc8ae6d754fdb7ae798b26f1cb02f234cf42937c3217ae585c484225cd3860f7b8ba929d9ecb0c2f9cc67f7ae8e449523ef35f1762723dcc5c89372278185
7
+ data.tar.gz: 6667deae41d668ac3c819ac9756ee9d92520ca97304921ff94b3f53dcccc88e3ff1a26439300590f2cd0a26fa308ef1454b588255a0982e277750ea786d9adcf
data/bin/sauce ADDED
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'cmdparse'
4
+ require 'yaml'
5
+ require 'fileutils'
6
+ require 'json'
7
+ require 'highline/import'
8
+
9
+ sauce_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
10
+ $LOAD_PATH.unshift(sauce_dir) unless $LOAD_PATH.include?(sauce_dir)
11
+
12
+ require 'sauce'
13
+
14
+ cmd = CmdParse::CommandParser.new(true, true)
15
+ cmd.program_name = "sauce "
16
+ cmd.program_version = [0, 1, 0]
17
+
18
+ cmd.add_command(CmdParse::HelpCommand.new)
19
+
20
+ class ConnectCommand < CmdParse::Command
21
+ def usage
22
+ "Usage: #{commandparser.program_name}connect"
23
+ end
24
+ end
25
+ connect = ConnectCommand.new('connect', false)
26
+ connect.short_desc = 'Connect a Sauce Connect tunnel'
27
+ connect.set_execution_block do |args|
28
+ begin
29
+ require 'sauce/connect'
30
+ rescue LoadError
31
+ puts "Please install the sauce-connect gem (`gem install sauce-connect`) in order to use Sauce Connect"
32
+ exit 1
33
+ end
34
+ require 'sauce/config'
35
+ config = Sauce::Config.new
36
+ command = "#{Sauce::Connect.connect_command} #{config.username} #{config.access_key} #{ARGV[1 .. -1]}"
37
+ exec(command)
38
+ end
39
+ cmd.add_command(connect)
40
+
41
+
42
+ # configure
43
+ class ConfigureCommand < CmdParse::Command
44
+ def usage
45
+ "Usage: #{commandparser.program_name}configure USERNAME ACCESS_KEY"
46
+ end
47
+ end
48
+ configure = ConfigureCommand.new('configure', false)
49
+ configure.short_desc = "Configure Sauce Labs credentials"
50
+ configure.set_execution_block do |args|
51
+ if args.length < 2
52
+ puts configure.usage
53
+ exit 1
54
+ end
55
+ username = args[0]
56
+ access_key = args[1]
57
+ dir = File.join(File.expand_path("~"), ".sauce")
58
+ FileUtils.mkdir(dir) unless File.directory?(dir)
59
+
60
+ out = File.new(File.join(dir, "ondemand.yml"), 'w')
61
+ out.write(YAML.dump({"username" => username, "access_key" => access_key}))
62
+ out.close()
63
+ end
64
+ cmd.add_command(configure)
65
+
66
+ #create
67
+ create = CmdParse::Command.new('create', false)
68
+ create.short_desc = "Create a new Sauce Labs account"
69
+ create.set_execution_block do |args|
70
+ puts "Let's create a new account!"
71
+ username = ask("Username: ")
72
+ password = ask("Password: ") { |q| q.echo = "*" }
73
+ email = ask("Email: ")
74
+ name = ask("Full name: ")
75
+
76
+ body = {
77
+ :username => username,
78
+ :password => password,
79
+ :email => email,
80
+ :name => name,
81
+ :token => "c8eb3e2645005bcbbce7e2c208c6b7a71555d908"
82
+ }.to_json
83
+
84
+ begin
85
+ result = RestClient.post("http://saucelabs.com/rest/v1/users",
86
+ body, :content_type => :json, :accept => :json)
87
+ rescue => e
88
+ begin
89
+ puts "ERROR: #{JSON.load(e.response)['errors']}"
90
+ rescue
91
+ puts "ERROR: #{e.response}"
92
+ end
93
+ else
94
+ result = JSON.load(result)
95
+
96
+ puts "Account #{username} created successfully."
97
+ puts "Your access key is: #{result['access_key']}"
98
+ end
99
+ end
100
+
101
+ cmd.add_command(create)
102
+
103
+ cmd.parse
@@ -0,0 +1,34 @@
1
+ module ChildProcess
2
+ module Unix
3
+ class Process < AbstractProcess
4
+
5
+ def return_unless_timeout
6
+ lambda do |timeout|
7
+ begin
8
+ return poll_for_exit timeout
9
+ rescue TimeoutError
10
+ end
11
+ end
12
+ end
13
+
14
+ def stop(timeout = 3, signal=nil)
15
+ assert_started
16
+
17
+ unless signal.nil?
18
+ send_signal signal
19
+ return_unless_timeout.call(timeout)
20
+ end
21
+
22
+ send_term
23
+ return_unless_timeout.call(timeout)
24
+
25
+ send_kill
26
+ wait
27
+ rescue Errno::ECHILD, Errno::ESRCH
28
+ # handle race condition where process dies between timeout
29
+ # and send_kill
30
+ true
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,54 @@
1
+ module Sauce
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ desc "Prep application for Sauce OnDemand Selenium tests"
7
+
8
+ argument :username, :type => :string, :required => false
9
+ argument :api_key, :type => :string, :required => false
10
+
11
+ def copy_rake_tasks
12
+ copy_file "sauce.rake", "lib/tasks/sauce.rake"
13
+ end
14
+
15
+ def configure_credentials
16
+ if username
17
+ system("sauce config #{username} #{api_key}")
18
+ end
19
+ end
20
+
21
+ def setup_spec
22
+ if File.directory? 'spec'
23
+ empty_directory "spec/selenium"
24
+ append_file "spec/spec_helper.rb", generate_config
25
+ end
26
+ end
27
+
28
+ def setup_test
29
+ if File.directory? 'test'
30
+ empty_directory "test/selenium"
31
+ append_file "test/test_helper.rb", generate_config
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def generate_config
38
+ return <<-CONFIG
39
+
40
+ require 'sauce'
41
+
42
+ Sauce.config do |conf|
43
+ conf[:browsers] = [
44
+ ["Windows 2003", "firefox", "3.6."]
45
+ ]
46
+ conf[:application_host] = "127.0.0.1"
47
+ conf[:application_port] = "3001"
48
+ conf[:browser_url] = "http://localhost:3001/"
49
+ end
50
+ CONFIG
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ module ParallelTests
2
+ class CLI
3
+ def run_tests_in_parallel(num_processes, options)
4
+ test_results = nil
5
+
6
+ report_time_taken do
7
+ groups = @runner.tests_in_groups(options[:files], num_processes, options)
8
+ non_empty_groups = groups.reject {|group| group.empty?}
9
+ Sauce::TestBroker.test_groups = non_empty_groups
10
+
11
+ report_number_of_tests(non_empty_groups)
12
+
13
+ test_results = execute_in_parallel(non_empty_groups, non_empty_groups.size, options) do |group|
14
+ run_tests(group, Sauce::TestBroker.group_index(group), num_processes, options)
15
+ end
16
+
17
+ report_results(test_results)
18
+ end
19
+
20
+ abort final_fail_message if any_test_failed?(test_results)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,43 @@
1
+ require "parallel_tests/cucumber/runner"
2
+
3
+ module ParallelTests
4
+ module Saucecucumber
5
+ class Runner < ParallelTests::Cucumber::Runner
6
+
7
+ def self.run_tests(test_files, process_number, num_processes, options)
8
+ options = options.dup
9
+ sanitized_test_files = test_files.map { |val| Shellwords.escape(val) }
10
+ env = Sauce::TestBroker.next_environment(test_files)
11
+ env.merge!({"AUTOTEST" => "1"}) if $stdout.tty? # display color when we are in a terminal
12
+ options.merge!({:env => env})
13
+ cmd = [
14
+ executable,
15
+ (runtime_logging if File.directory?(File.dirname(runtime_log))),
16
+ cucumber_opts(options[:test_options]),
17
+ *sanitized_test_files
18
+ ].compact.join(" ")
19
+ execute_command(cmd, process_number, num_processes, options)
20
+ end
21
+
22
+ def self.tests_in_groups(tests, num_groups, options={})
23
+ originals = (options[:group_by] == :steps) ? Grouper.by_steps(find_tests(tests, options), num_groups, options) : super
24
+ all_tests = originals.flatten * Sauce::TestBroker.test_platforms(:cucumber).length
25
+ base_group_size = all_tests.length / num_groups
26
+ num_full_groups = all_tests.length - (base_group_size * num_groups)
27
+
28
+ curpos = 0
29
+ groups = []
30
+ num_groups.times do |i|
31
+ group_size = base_group_size
32
+ if i < num_full_groups
33
+ group_size += 1
34
+ end
35
+ groups << all_tests.slice(curpos, group_size)
36
+ curpos += group_size
37
+ end
38
+
39
+ groups
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ require "yaml"
2
+ require "parallel_tests/rspec/runner"
3
+
4
+ module ParallelTests
5
+ module Saucerspec
6
+ class Runner < ParallelTests::RSpec::Runner
7
+
8
+ def self.run_tests(test_files, process_number, num_processes, options)
9
+ our_options = options.dup
10
+ exe = executable # expensive, so we cache
11
+ version = (exe =~ /\brspec\b/ ? 2 : 1)
12
+ cmd = [exe, our_options[:test_options], (rspec_2_color if version == 2), spec_opts, *test_files].compact.join(" ")
13
+ env = Sauce::TestBroker.next_environment(test_files)
14
+ env << " #{rspec_1_color}" if version == 1
15
+ our_options.merge!(:env => env)
16
+ execute_command(cmd, process_number, num_processes, our_options)
17
+ end
18
+
19
+
20
+ def self.tests_in_groups(tests, num_groups, options={})
21
+ all_tests = super.flatten * Sauce::TestBroker.test_platforms(:rspec).length
22
+ base_group_size = all_tests.length / num_groups
23
+ num_full_groups = all_tests.length - (base_group_size * num_groups)
24
+
25
+ curpos = 0
26
+ groups = []
27
+ num_groups.times do |i|
28
+ group_size = base_group_size
29
+ if i < num_full_groups
30
+ group_size += 1
31
+ end
32
+ groups << all_tests.slice(curpos, group_size)
33
+ curpos += group_size
34
+ end
35
+
36
+ groups
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/sauce.rb ADDED
@@ -0,0 +1,33 @@
1
+ if defined?(Rails)
2
+ require "sauce/railtie"
3
+ else
4
+ require 'tasks/parallel_testing'
5
+ end
6
+
7
+ require 'sauce/version'
8
+ require 'sauce/utilities'
9
+ require 'sauce/utilities/rake'
10
+ require 'sauce/job'
11
+ require 'sauce/client'
12
+ require 'sauce/config'
13
+ require 'sauce/selenium'
14
+ require 'sauce/test_base'
15
+ require 'sauce/rspec/rspec'
16
+ require 'sauce/test_unit'
17
+ require 'parallel_tests/saucerspec/runner'
18
+ require 'parallel_tests/saucecucumber/runner'
19
+
20
+
21
+ # Ruby before 1.9.3-p382 does not handle exit codes correctly when nested
22
+ if RUBY_VERSION == "1.9.3" && RUBY_PATCHLEVEL < 392
23
+ module Kernel
24
+ alias :existing_at_exit :at_exit
25
+ def at_exit(&block)
26
+ existing_at_exit do
27
+ exit_status = $!.status if $!.is_a?(SystemExit)
28
+ block.call
29
+ exit exit_status if exit_status
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,189 @@
1
+ require 'capybara'
2
+ require 'sauce/config'
3
+ require 'sauce/selenium'
4
+ require 'sauce/version'
5
+
6
+
7
+ $sauce_tunnel = nil
8
+
9
+ module Sauce
10
+ module Capybara
11
+ class Driver < ::Capybara::Selenium::Driver
12
+ RETRY_ON = [::Selenium::WebDriver::Error::UnhandledError,
13
+ ::Selenium::WebDriver::Error::UnknownError]
14
+ MAX_RETRIES = 3
15
+
16
+ def handle_retry(method, *args, &block)
17
+ retries = 0
18
+
19
+ # Disable retries only when we really really want to, this will remain
20
+ # an undocomented hack for the time being
21
+ if ENV['SAUCE_DISABLE_RETRY']
22
+ retries = MAX_RETRIES
23
+ end
24
+
25
+ begin
26
+ send("base_#{method}".to_sym, *args, &block)
27
+ rescue *RETRY_ON => e
28
+ if retries < MAX_RETRIES
29
+ puts "Received an exception (#{e}), retrying"
30
+ retries = retries + 1
31
+ retry
32
+ else
33
+ raise
34
+ end
35
+ end
36
+ end
37
+
38
+ alias :base_visit :visit
39
+ alias :base_current_url :current_url
40
+ alias :base_within_frame :within_frame
41
+ alias :base_within_window :within_window
42
+ alias :base_find_window :find_window
43
+ alias :base_execute_script :execute_script
44
+ alias :base_evaluate_script :evaluate_script
45
+
46
+ @methods_to_retry = [:visit, :current_url,
47
+ :within_frame, :within_window, :find_window, :source,
48
+ :execute_script, :evaluate_script
49
+ ]
50
+
51
+ if method_defined? :find
52
+ alias :base_find :find
53
+ @methods_to_retry += [:find]
54
+ else
55
+ alias :base_find_css :find_css
56
+ alias :base_find_xpath :find_xpath
57
+ @methods_to_retry += [:find_css, :find_xpath]
58
+ end
59
+
60
+ if Gem::Version.new(::Capybara::VERSION) < Gem::Version.new(2)
61
+ alias :base_body :body
62
+ alias :base_source :source
63
+
64
+ @methods_to_retry + [:body, :source]
65
+ else
66
+ alias :base_html :html
67
+ @methods_to_retry + [:html]
68
+ end
69
+
70
+ @methods_to_retry.each do |method|
71
+ define_method(method) do |*args, &block|
72
+ handle_retry(method, *args, &block)
73
+ end
74
+ end
75
+
76
+ # Oh gods why didn't I comment these when I wrote them?
77
+ # These are what I think I'm doing.
78
+ #
79
+ # Returns the browser currently being used or fetches a new
80
+ # browser, either from the RSpec integration or by creating
81
+ # one.
82
+ def browser
83
+ # Return the existing browser if we have it
84
+ unless existing_browser?
85
+ # Try to get a driver from the driver pool
86
+ @browser = rspec_browser
87
+ unless @browser
88
+ @browser = Sauce::Selenium2.new
89
+ at_exit do
90
+ finish!
91
+ end
92
+ end
93
+ end
94
+ @browser
95
+ end
96
+
97
+ # Returns the rspec created browser if it exists
98
+ def rspec_browser
99
+ if browser = Sauce.driver_pool[Thread.current.object_id]
100
+ @using_rspec_browser = true
101
+ else
102
+ @using_rspec_browser = false
103
+ end
104
+ browser
105
+ end
106
+
107
+ # If a browser has been created OR RSpec has put one in the diriver pool
108
+ # and we're using that browser, returns true.
109
+ def existing_browser?
110
+ if @using_rspec_browser
111
+ @browser == Sauce.driver_pool[Thread.current.object_id]
112
+ else
113
+ @browser
114
+ end
115
+ end
116
+
117
+ def finish!
118
+ @browser.quit if existing_browser?
119
+ @browser = nil
120
+
121
+ # Rethink how to do this. RSpec still references the driver pool.
122
+ Sauce.driver_pool[Thread.current.object_id] = nil
123
+ @using_rspec_browser = nil
124
+ end
125
+
126
+ def render(path)
127
+ browser.save_screenshot path
128
+ end
129
+ end
130
+
131
+ def self.configure_capybara
132
+ ::Capybara.configure do |config|
133
+ if Sauce::Config.new[:start_local_application]
134
+ config.server_port = Sauce::Config.get_application_port
135
+ end
136
+ begin
137
+ #config.always_include_port = true
138
+ rescue
139
+ # This option is only in Capybara 2+
140
+ end
141
+ end
142
+ end
143
+
144
+ def self.configure_capybara_for_rspec
145
+ begin
146
+ require "rspec/core"
147
+ ::RSpec.configure do |config|
148
+ config.before :suite do
149
+ ::Capybara.configure do |capy_config|
150
+ sauce_config = Sauce::Config.new
151
+ if capy_config.app_host.nil?
152
+ if sauce_config[:start_local_application]
153
+ host = sauce_config[:application_host] || "127.0.0.1"
154
+ port = sauce_config[:application_port]
155
+ capy_config.app_host = "http://#{host}:#{port}"
156
+ capy_config.run_server = false
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ rescue LoadError => e
163
+ # User is not using RSpec
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ Capybara.register_driver :sauce do |app|
170
+ driver = Sauce::Capybara::Driver.new(app)
171
+ end
172
+
173
+ Sauce::Capybara.configure_capybara
174
+
175
+ # Monkeypatch Capybara to not use :selenium driver
176
+ require 'capybara/dsl'
177
+ module Capybara
178
+ def self.javascript_driver
179
+ @javascript_driver || :sauce
180
+ end
181
+ end
182
+
183
+ module Sauce
184
+ module RSpec
185
+ module SeleniumExampleGroup
186
+ Sauce::Capybara.configure_capybara_for_rspec
187
+ end
188
+ end
189
+ end