sauce 1.0.2 → 2.0.0

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