sauce 1.0.2 → 2.0.0

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 (56) hide show
  1. data/.document +5 -0
  2. data/.gitignore +30 -0
  3. data/Gemfile +16 -0
  4. data/README.markdown +39 -145
  5. data/Rakefile +46 -20
  6. data/bin/sauce +72 -61
  7. data/gemfiles/rails2.gemfile +10 -0
  8. data/gemfiles/rails2.gemfile.lock +77 -0
  9. data/gemfiles/rails3.gemfile +9 -0
  10. data/gemfiles/rails3.gemfile.lock +137 -0
  11. data/lib/generators/sauce/install/install_generator.rb +1 -2
  12. data/lib/sauce.rb +0 -22
  13. data/lib/sauce/capybara.rb +70 -32
  14. data/lib/sauce/capybara/cucumber.rb +121 -0
  15. data/lib/sauce/config.rb +57 -13
  16. data/lib/sauce/connect.rb +22 -11
  17. data/lib/sauce/integrations.rb +27 -69
  18. data/lib/sauce/jasmine.rb +35 -0
  19. data/lib/sauce/jasmine/rake.rb +47 -0
  20. data/lib/sauce/jasmine/runner.rb +4 -0
  21. data/lib/sauce/job.rb +10 -6
  22. data/lib/sauce/raketasks.rb +0 -21
  23. data/lib/sauce/selenium.rb +9 -18
  24. data/lib/sauce/utilities.rb +0 -17
  25. data/sauce.gemspec +8 -60
  26. data/spec/integration/connect_integration_spec.rb +84 -0
  27. data/spec/sauce/capybara/cucumber_spec.rb +156 -0
  28. data/spec/sauce/capybara/spec_helper.rb +42 -0
  29. data/spec/sauce/capybara_spec.rb +121 -0
  30. data/spec/sauce/config_spec.rb +239 -0
  31. data/spec/sauce/jasmine_spec.rb +49 -0
  32. data/spec/sauce/selenium_spec.rb +57 -0
  33. data/spec/spec_helper.rb +4 -0
  34. data/support/Sauce-Connect.jar +0 -0
  35. data/test/test_integrations.rb +202 -0
  36. data/test/test_testcase.rb +13 -0
  37. metadata +170 -171
  38. data/examples/helper.rb +0 -16
  39. data/examples/other_spec.rb +0 -7
  40. data/examples/saucelabs_spec.rb +0 -12
  41. data/examples/test_saucelabs.rb +0 -13
  42. data/examples/test_saucelabs2.rb +0 -9
  43. data/support/sauce_connect +0 -938
  44. data/support/selenium-server.jar +0 -0
  45. data/support/simplejson/LICENSE.txt +0 -19
  46. data/support/simplejson/__init__.py +0 -437
  47. data/support/simplejson/decoder.py +0 -421
  48. data/support/simplejson/encoder.py +0 -501
  49. data/support/simplejson/ordered_dict.py +0 -119
  50. data/support/simplejson/scanner.py +0 -77
  51. data/support/simplejson/tool.py +0 -39
  52. data/test/test_config.rb +0 -112
  53. data/test/test_connect.rb +0 -45
  54. data/test/test_job.rb +0 -13
  55. data/test/test_selenium.rb +0 -50
  56. data/test/test_selenium2.rb +0 -9
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :gemcutter
4
+
5
+ gem "rspec", "1.3.2"
6
+ gem "rspec-rails", "1.3.4"
7
+ gem "rails", "2.3.14"
8
+ gem "sqlite3"
9
+
10
+ gemspec :path=>"../"
@@ -0,0 +1,77 @@
1
+ PATH
2
+ remote: /home/tyler/source/github/sauce_ruby
3
+ specs:
4
+ sauce (1.1.0)
5
+ childprocess (>= 0.1.6)
6
+ cmdparse (>= 2.0.2)
7
+ highline (>= 1.5.0)
8
+ json (>= 1.2.0)
9
+ net-http-persistent
10
+ net-ssh
11
+ net-ssh-gateway
12
+ rest-client
13
+ selenium-webdriver (>= 0.1.4)
14
+
15
+ GEM
16
+ remote: http://rubygems.org/
17
+ specs:
18
+ actionmailer (2.3.14)
19
+ actionpack (= 2.3.14)
20
+ actionpack (2.3.14)
21
+ activesupport (= 2.3.14)
22
+ rack (~> 1.1.0)
23
+ activerecord (2.3.14)
24
+ activesupport (= 2.3.14)
25
+ activeresource (2.3.14)
26
+ activesupport (= 2.3.14)
27
+ activesupport (2.3.14)
28
+ appraisal (0.4.1)
29
+ bundler
30
+ rake
31
+ childprocess (0.3.1)
32
+ ffi (~> 1.0.6)
33
+ cmdparse (2.0.3)
34
+ ffi (1.0.11)
35
+ highline (1.6.11)
36
+ json (1.6.6)
37
+ mime-types (1.18)
38
+ multi_json (1.2.0)
39
+ net-http-persistent (2.6)
40
+ net-ssh (2.3.0)
41
+ net-ssh-gateway (1.1.0)
42
+ net-ssh (>= 1.99.1)
43
+ rack (1.1.3)
44
+ rails (2.3.14)
45
+ actionmailer (= 2.3.14)
46
+ actionpack (= 2.3.14)
47
+ activerecord (= 2.3.14)
48
+ activeresource (= 2.3.14)
49
+ activesupport (= 2.3.14)
50
+ rake (>= 0.8.3)
51
+ rake (0.9.2.2)
52
+ rest-client (1.6.7)
53
+ mime-types (>= 1.16)
54
+ rspec (1.3.2)
55
+ rspec-rails (1.3.4)
56
+ rack (>= 1.0.0)
57
+ rspec (~> 1.3.1)
58
+ rubyzip (0.9.6.1)
59
+ selenium-webdriver (2.20.0)
60
+ childprocess (>= 0.2.5)
61
+ ffi (~> 1.0)
62
+ multi_json (~> 1.0)
63
+ rubyzip
64
+ sqlite3 (1.3.5)
65
+
66
+ PLATFORMS
67
+ ruby
68
+
69
+ DEPENDENCIES
70
+ appraisal
71
+ bundler
72
+ rails (= 2.3.14)
73
+ rake
74
+ rspec (= 1.3.2)
75
+ rspec-rails (= 1.3.4)
76
+ sauce!
77
+ sqlite3
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :gemcutter
4
+
5
+ gem "rspec"
6
+ gem "rspec-rails"
7
+ gem "rails", "3.2.0"
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,137 @@
1
+ PATH
2
+ remote: /home/tyler/source/github/sauce_ruby
3
+ specs:
4
+ sauce (1.1.0)
5
+ childprocess (>= 0.1.6)
6
+ cmdparse (>= 2.0.2)
7
+ highline (>= 1.5.0)
8
+ json (>= 1.2.0)
9
+ net-http-persistent
10
+ net-ssh
11
+ net-ssh-gateway
12
+ rest-client
13
+ selenium-webdriver (>= 0.1.4)
14
+
15
+ GEM
16
+ remote: http://rubygems.org/
17
+ specs:
18
+ actionmailer (3.2.0)
19
+ actionpack (= 3.2.0)
20
+ mail (~> 2.4.0)
21
+ actionpack (3.2.0)
22
+ activemodel (= 3.2.0)
23
+ activesupport (= 3.2.0)
24
+ builder (~> 3.0.0)
25
+ erubis (~> 2.7.0)
26
+ journey (~> 1.0.0)
27
+ rack (~> 1.4.0)
28
+ rack-cache (~> 1.1)
29
+ rack-test (~> 0.6.1)
30
+ sprockets (~> 2.1.2)
31
+ activemodel (3.2.0)
32
+ activesupport (= 3.2.0)
33
+ builder (~> 3.0.0)
34
+ activerecord (3.2.0)
35
+ activemodel (= 3.2.0)
36
+ activesupport (= 3.2.0)
37
+ arel (~> 3.0.0)
38
+ tzinfo (~> 0.3.29)
39
+ activeresource (3.2.0)
40
+ activemodel (= 3.2.0)
41
+ activesupport (= 3.2.0)
42
+ activesupport (3.2.0)
43
+ i18n (~> 0.6)
44
+ multi_json (~> 1.0)
45
+ appraisal (0.4.1)
46
+ bundler
47
+ rake
48
+ arel (3.0.2)
49
+ builder (3.0.0)
50
+ childprocess (0.3.1)
51
+ ffi (~> 1.0.6)
52
+ cmdparse (2.0.3)
53
+ diff-lcs (1.1.3)
54
+ erubis (2.7.0)
55
+ ffi (1.0.11)
56
+ highline (1.6.11)
57
+ hike (1.2.1)
58
+ i18n (0.6.0)
59
+ journey (1.0.3)
60
+ json (1.6.6)
61
+ mail (2.4.4)
62
+ i18n (>= 0.4.0)
63
+ mime-types (~> 1.16)
64
+ treetop (~> 1.4.8)
65
+ mime-types (1.18)
66
+ multi_json (1.2.0)
67
+ net-http-persistent (2.6)
68
+ net-ssh (2.3.0)
69
+ net-ssh-gateway (1.1.0)
70
+ net-ssh (>= 1.99.1)
71
+ polyglot (0.3.3)
72
+ rack (1.4.1)
73
+ rack-cache (1.2)
74
+ rack (>= 0.4)
75
+ rack-ssl (1.3.2)
76
+ rack
77
+ rack-test (0.6.1)
78
+ rack (>= 1.0)
79
+ rails (3.2.0)
80
+ actionmailer (= 3.2.0)
81
+ actionpack (= 3.2.0)
82
+ activerecord (= 3.2.0)
83
+ activeresource (= 3.2.0)
84
+ activesupport (= 3.2.0)
85
+ bundler (~> 1.0)
86
+ railties (= 3.2.0)
87
+ railties (3.2.0)
88
+ actionpack (= 3.2.0)
89
+ activesupport (= 3.2.0)
90
+ rack-ssl (~> 1.3.2)
91
+ rake (>= 0.8.7)
92
+ rdoc (~> 3.4)
93
+ thor (~> 0.14.6)
94
+ rake (0.9.2.2)
95
+ rdoc (3.12)
96
+ json (~> 1.4)
97
+ rest-client (1.6.7)
98
+ mime-types (>= 1.16)
99
+ rspec (2.9.0)
100
+ rspec-core (~> 2.9.0)
101
+ rspec-expectations (~> 2.9.0)
102
+ rspec-mocks (~> 2.9.0)
103
+ rspec-core (2.9.0)
104
+ rspec-expectations (2.9.1)
105
+ diff-lcs (~> 1.1.3)
106
+ rspec-mocks (2.9.0)
107
+ rspec-rails (1.3.2)
108
+ rack (>= 1.0.0)
109
+ rspec (>= 1.3.0)
110
+ rubyzip (0.9.6.1)
111
+ selenium-webdriver (2.20.0)
112
+ childprocess (>= 0.2.5)
113
+ ffi (~> 1.0)
114
+ multi_json (~> 1.0)
115
+ rubyzip
116
+ sprockets (2.1.2)
117
+ hike (~> 1.2)
118
+ rack (~> 1.0)
119
+ tilt (~> 1.1, != 1.3.0)
120
+ thor (0.14.6)
121
+ tilt (1.3.3)
122
+ treetop (1.4.10)
123
+ polyglot
124
+ polyglot (>= 0.3.1)
125
+ tzinfo (0.3.32)
126
+
127
+ PLATFORMS
128
+ ruby
129
+
130
+ DEPENDENCIES
131
+ appraisal
132
+ bundler
133
+ rails (= 3.2.0)
134
+ rake
135
+ rspec
136
+ rspec-rails
137
+ sauce!
@@ -35,18 +35,17 @@ module Sauce
35
35
  private
36
36
 
37
37
  def generate_config
38
- @random_id ||= rand(100000)
39
38
  return <<-CONFIG
40
39
 
41
40
  require 'sauce'
42
41
 
43
42
  Sauce.config do |conf|
44
- conf.browser_url = "http://#{@random_id}.test/"
45
43
  conf.browsers = [
46
44
  ["Windows 2003", "firefox", "3.6."]
47
45
  ]
48
46
  conf.application_host = "127.0.0.1"
49
47
  conf.application_port = "3001"
48
+ conf.browser_url = "http://localhost:3001/"
50
49
  end
51
50
  CONFIG
52
51
  end
data/lib/sauce.rb CHANGED
@@ -7,26 +7,4 @@ require 'sauce/integrations'
7
7
  require 'sauce/connect'
8
8
 
9
9
  module Sauce
10
- @@cached_sessions = {}
11
-
12
- def self.cached_session(opts)
13
- @@cached_sessions[opts] or new_session(opts)
14
- end
15
-
16
- private
17
-
18
- def self.new_session(opts)
19
- session = nil
20
- if Sauce::Config.new.local?
21
- session = ::Selenium::Client::Driver.new(opts)
22
- else
23
- session = Sauce::Selenium.new(opts)
24
- end
25
- at_exit do
26
- session.stop
27
- end
28
- session.start
29
- @@cached_sessions[opts] = session
30
- return session
31
- end
32
10
  end
@@ -1,40 +1,86 @@
1
1
  require 'capybara'
2
- require 'uri'
3
2
 
4
- $uri = URI.parse Capybara.app_host || ""
3
+ require 'sauce/config'
4
+ require 'sauce/connect'
5
+ require 'sauce/selenium'
6
+
7
+
8
+ $sauce_tunnel = nil
5
9
 
6
10
  module Sauce
7
11
  module Capybara
12
+ def connect_tunnel(options={})
13
+ unless $sauce_tunnel.nil?
14
+ return $sauce_tunnel
15
+ end
16
+ $sauce_tunnel = Sauce::Connect.new(options)
17
+ $sauce_tunnel.connect
18
+ $sauce_tunnel.wait_until_ready
19
+ $sauce_tunnel
20
+ end
21
+ module_function :connect_tunnel
22
+
8
23
  class Driver < ::Capybara::Selenium::Driver
9
- def browser
10
- unless @browser
11
- puts "[Connecting to Sauce OnDemand...]"
12
- config = Sauce::Config.new
13
- @domain = "#{rand(10000)}.test"
14
- @sauce_tunnel = Sauce::Connect.new(:host => $uri.host || rack_server.host,
15
- :port => $uri.port || rack_server.port,
16
- :domain => @domain,
17
- :quiet => true)
18
- @sauce_tunnel.wait_until_ready
19
- @browser = Sauce::Selenium2.new(:name => "Capybara test",
20
- :browser_url => "http://#{@domain}")
21
- at_exit do
22
- @browser.quit
23
- @sauce_tunnel.disconnect
24
+ RETRY_ON = [::Selenium::WebDriver::Error::UnhandledError,
25
+ ::Selenium::WebDriver::Error::UnknownError]
26
+ MAX_RETRIES = 3
27
+
28
+ def handle_retry(method, *args)
29
+ retries = 0
30
+
31
+ # Disable retries only when we really really want to, this will remain
32
+ # an undocomented hack for the time being
33
+ if ENV['SAUCE_DISABLE_RETRY']
34
+ retries = MAX_RETRIES
35
+ end
36
+
37
+ begin
38
+ send("base_#{method}".to_sym, *args)
39
+ rescue *RETRY_ON => e
40
+ if retries < MAX_RETRIES
41
+ puts "Received an exception (#{e}), retrying"
42
+ retries = retries + 1
43
+ retry
44
+ else
45
+ raise
24
46
  end
25
47
  end
26
- @browser
27
48
  end
28
49
 
29
- private
50
+ alias :base_find :find
51
+ alias :base_visit :visit
52
+ alias :base_current_url :current_url
53
+ alias :base_reset! :reset!
54
+ alias :base_within_frame :within_frame
55
+ alias :base_within_window :within_window
56
+ alias :base_find_window :find_window
57
+ alias :base_body :body
58
+ alias :base_source :source
59
+ alias :base_execute_script :execute_script
60
+ alias :base_evaluate_script :evaluate_script
30
61
 
31
- def url(path)
32
- if path =~ /^http/
33
- path
34
- else
35
- "http://#{@domain}#{path}"
62
+ [:find, :visit, :current_url, :reset!, :within_frame,
63
+ :within_window, :find_window, :body, :source,
64
+ :execute_script, :evaluate_script].each do |method|
65
+ define_method(method) do |*args|
66
+ handle_retry(method, *args)
36
67
  end
37
68
  end
69
+
70
+ def browser
71
+ unless @browser
72
+ if Sauce.get_config[:start_tunnel]
73
+ Sauce::Capybara.connect_tunnel(:quiet => true)
74
+ end
75
+
76
+ @browser = Sauce::Selenium2.new
77
+ at_exit do
78
+ @browser.quit if @browser
79
+ $sauce_tunnel.disconnect if $sauce_tunnel
80
+ end
81
+ end
82
+ @browser
83
+ end
38
84
  end
39
85
  end
40
86
  end
@@ -50,11 +96,3 @@ module Capybara
50
96
  @javascript_driver || :sauce
51
97
  end
52
98
  end
53
-
54
- # Switch Cucumber stories tagged with @selenium to use sauce
55
- begin
56
- Before("@selenium") do
57
- Capybara.current_driver = :sauce
58
- end
59
- rescue NoMethodError
60
- end
@@ -0,0 +1,121 @@
1
+ require 'capybara'
2
+ require 'cucumber'
3
+ require 'sauce/job'
4
+
5
+ module Sauce
6
+ module Capybara
7
+ module Cucumber
8
+ def use_sauce_driver
9
+ ::Capybara.current_driver = :sauce
10
+ end
11
+ module_function :use_sauce_driver
12
+
13
+ def name_from_scenario(scenario)
14
+ # Special behavior to handle Scenario Outlines
15
+ if scenario.instance_of? ::Cucumber::Ast::OutlineTable::ExampleRow
16
+ table = scenario.instance_variable_get(:@table)
17
+ outline = table.instance_variable_get(:@scenario_outline)
18
+ return "#{outline.feature.file} - #{outline.title} - #{table.headers} -> #{scenario.name}"
19
+ end
20
+ scenario, feature = _scenario_and_feature_name(scenario)
21
+ return "#{feature} - #{scenario}"
22
+ end
23
+ module_function :name_from_scenario
24
+
25
+ def jenkins_name_from_scenario(scenario)
26
+ # Special behavior to handle Scenario Outlines
27
+ if scenario.instance_of? ::Cucumber::Ast::OutlineTable::ExampleRow
28
+ table = scenario.instance_variable_get(:@table)
29
+ outline = table.instance_variable_get(:@scenario_outline)
30
+ return "#{outline.feature.short_name}.#{outline.title}.#{outline.title} (outline example: #{scenario.name})"
31
+ end
32
+ scenario, feature = _scenario_and_feature_name(scenario)
33
+ return "#{feature}.#{scenario}.#{scenario}"
34
+ end
35
+ module_function :jenkins_name_from_scenario
36
+
37
+ def _scenario_and_feature_name(scenario)
38
+ scenario_name = scenario.name.split("\n").first
39
+ feature_name = scenario.feature.short_name
40
+ return scenario_name, feature_name
41
+ end
42
+ module_function :_scenario_and_feature_name
43
+
44
+ def before_hook
45
+ Sauce::Capybara::Cucumber.use_sauce_driver
46
+ end
47
+ module_function :before_hook
48
+
49
+ def using_jenkins?
50
+ # JENKINS_SERVER_COOKIE seems to be as good as any sentinel value to
51
+ # determine whether we're running under Jenkins or not.
52
+ ENV['JENKINS_SERVER_COOKIE']
53
+ end
54
+ module_function :using_jenkins?
55
+
56
+ def around_hook(scenario, block)
57
+ ::Capybara.current_driver = :sauce
58
+ driver = ::Capybara.current_session.driver
59
+ # This session_id is the job ID used by Sauce Labs, we're pulling it
60
+ # off of the driver now to make sure we have it after `block.call`
61
+ session_id = driver.browser.session_id
62
+ job_name = Sauce::Capybara::Cucumber.name_from_scenario(scenario)
63
+ custom_data = {}
64
+
65
+ if using_jenkins?
66
+ custom_data.merge!({:commit => ENV['GIT_COMMIT'] || ENV['SVN_COMMIT'],
67
+ :jenkins_node => ENV['NODE_NAME'],
68
+ :jenkins_job => ENV['JOB_NAME']})
69
+ end
70
+
71
+ job = Sauce::Job.new('id' => session_id,
72
+ 'name' => job_name,
73
+ 'custom-data' => custom_data)
74
+ job.save unless job.nil?
75
+
76
+ Sauce.config do |c|
77
+ c[:name] = Sauce::Capybara::Cucumber.name_from_scenario(scenario)
78
+ end
79
+
80
+ if using_jenkins?
81
+ # If we're running under Jenkins, we should dump the
82
+ # `SauceOnDemandSessionID` into the Console Output for the Sauce OnDemand
83
+ # Jenkins plugin.
84
+ # See: <https://github.com/saucelabs/sauce_ruby/issues/48>
85
+ output = []
86
+ output << "\nSauceOnDemandSessionID=#{session_id}"
87
+ # The duplication in the scenario_name comes from the fact that the
88
+ # JUnit formatter seems to do the same, so in order to get the sauce
89
+ # OnDemand plugin for Jenkins to co-operate, we need to double it up as
90
+ # well
91
+ output << "job-name=#{Sauce::Capybara::Cucumber.jenkins_name_from_scenario(scenario)}"
92
+ puts output.join(' ')
93
+ end
94
+
95
+ block.call
96
+
97
+ # Quit the driver to allow for the generation of a new session_id
98
+ driver.browser.quit
99
+ driver.instance_variable_set(:@browser, nil)
100
+
101
+ unless job.nil?
102
+ job.passed = !scenario.failed?
103
+ job.save
104
+ end
105
+ end
106
+ module_function :around_hook
107
+ end
108
+ end
109
+ end
110
+
111
+
112
+ begin
113
+ Before('@selenium') do
114
+ Sauce::Capybara::Cucumber.before_hook
115
+ end
116
+
117
+ Around('@selenium') do |scenario, block|
118
+ Sauce::Capybara::Cucumber.around_hook(scenario, block)
119
+ end
120
+ rescue NoMethodError # This makes me sad
121
+ end