sauce_bindings 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4f5442812caea4803e008a9eefa48c95401eff9f26f1dc4b649b480afc2ed745
4
+ data.tar.gz: 3772c4032b1f44cec5ddbb26f8e70ce3f75543fcf5668145bbd42cee49bdf76b
5
+ SHA512:
6
+ metadata.gz: 9e2d316260e0f3dfc0223546b11f87d635594fe6dfa5fe413c2491e0c8f58838d9fe3e7510299b88037a46cd8fac53b0142b5caeeb84fb80819c8bf5b1e884fc
7
+ data.tar.gz: 3e4dbae2ec880b4401efe7c67e9a9c5545d7a044e8d6f75005c3289c01df2df0404fbf20e11b142ff07c411dc454dd01029e49166d8ee036fa311094ee5fe3cb
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /.idea/
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
13
+
14
+ #ignore Gemfile.lock
15
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,49 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5.3
3
+
4
+ require:
5
+ - rubocop-rspec
6
+ - rubocop-performance
7
+
8
+ Layout/SpaceInsideHashLiteralBraces:
9
+ EnforcedStyle: no_space
10
+
11
+ Metrics/AbcSize:
12
+ Max: 22
13
+
14
+ Metrics/BlockLength:
15
+ Exclude:
16
+ - "**/*_spec.rb"
17
+
18
+ Metrics/CyclomaticComplexity:
19
+ Max: 7
20
+
21
+ Metrics/PerceivedComplexity:
22
+ Max: 8
23
+
24
+ Metrics/ClassLength:
25
+ Exclude:
26
+ - 'lib/sauce_bindings/options.rb'
27
+
28
+ Metrics/LineLength:
29
+ Max: 120
30
+
31
+ Metrics/MethodLength:
32
+ Max: 16
33
+
34
+ Metrics/ModuleLength:
35
+ CountComments: false
36
+ Exclude:
37
+ - 'spec/**/*'
38
+
39
+ Style/Documentation:
40
+ Enabled: false
41
+
42
+ RSpec/ExampleLength:
43
+ Enabled: false
44
+
45
+ RSpec/MultipleExpectations:
46
+ Enabled: false
47
+
48
+ RSpec/DescribedClass:
49
+ EnforcedStyle: explicit
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.3
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in sauce_bindings.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Titus Fortner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # SimpleSauce
2
+ ## Ruby's Sauce Labs Bindings!
3
+
4
+ This gem is intended as a way to easily interact with Sauce Labs in an obvious and straightforward way.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'sauce_bindings'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install sauce_bindings
21
+
22
+ ## Running Tests
23
+
24
+ To run tests for the Ruby Sauce Bindings, execute
25
+
26
+ $ bundle exec rake
27
+
28
+ ## Usage
29
+
30
+ `SimpleSauce` is broken into two main components, `Options` and `Session`
31
+
32
+ ### Options class
33
+ `Options` provides an easy interface to the Sauce Labs specific settings you want in your tests.
34
+
35
+ If you want the default settings (the latest Chrome version on Windows 10) with no other settings,
36
+ this is all the code you need:
37
+ ```
38
+ sauce_opts = SimpleSauce::Options.new
39
+ ```
40
+ To specify additional
41
+ [Sauce Labs supported settings](https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options),
42
+ simply pass these in as a `Hash`, or use an accessor:
43
+ ```
44
+ sauce_options = {screen_resolution: '1080x720',
45
+ browser_name: 'Firefox',
46
+ platform_name: 'Mac OS'}
47
+ sauce_opts = SimpleSauce::Options.new(sauce_options)
48
+ sauce_opts.idle_timeout = 100
49
+ ```
50
+ if you have additional browser specific settings, or
51
+ [webdriver w3c spec compliant settings](http://w3c.github.io/webdriver/webdriver-spec.html#capabilities), in
52
+ Selenium 3.x you need to generate each of these separately; see the `Session` class below for how to use it:
53
+ ```
54
+ sauce_options = SimpleSauce::Options.new(screen_resolution: '1080x720',
55
+ browser_name: 'Firefox',
56
+ platform_name: 'Mac OS')
57
+
58
+ selenium_options = Selenium::WebDriver::Remote::Capabilities.new(accept_insecure_certs: false,
59
+ page_load_strategy: 'eager'}
60
+
61
+ browser_options = Selenium::WebDriver::Firefox::Options.new('args' => ['-foo'])
62
+ ```
63
+
64
+ ### Session class
65
+ #### Intializing a Session
66
+ `Session` class gets initialized with an `Options` instance. If you want the
67
+ default settings (latest Chrome version on Windows 10), you don't even need to use an `Options` instance:
68
+ ```ruby
69
+ @session = SauceBindings::Session.new
70
+ ```
71
+ If you want something other than the default, create a Sauce `Option` instance (as generated in the section above,
72
+ then pass it into the constructor:
73
+ ```ruby
74
+ @session = SauceBindings::Session.new(sauce_opts)
75
+ ```
76
+ If you also have Selenium or Browser options as described above, then you pass them in with an `Array` like so:
77
+ ```ruby
78
+ @session = SauceBindings::Session.new([sauce_opts, se_opts, browser_opts])
79
+ ```
80
+
81
+ #### Creating a driver instance
82
+ The `#start` method is required, and will return a `Selenium::WebDriver::<Browser>::Driver` instance,
83
+ and the `#stop` method will automatically quit the driver as well as ending the Sauce Labs session.
84
+ ```ruby
85
+ session = SauceBindings::Session.new
86
+ driver = session.start
87
+ # Use the driver
88
+ session.stop
89
+ ```
90
+ Note that the `Driver` instance can also be obtained at any time with the `#driver` method:
91
+ ```ruby
92
+ session = SauceBindings::Session.new
93
+ session.start
94
+ driver = session.driver
95
+ # Use the driver
96
+ session.stop
97
+ ```
98
+ #### Additional Session methods
99
+ We have a number of features we plan to add; stay tuned to see what additional features are made available. If you
100
+ have any request, please contact the Sauce Labs Solution Architect team.
101
+
102
+ ## Contributing
103
+
104
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sauce/sauce_bindings.
105
+ This project is intended to be a safe, welcoming space for collaboration,
106
+ and contributors are expected to adhere to the
107
+ [Contributor Covenant](http://contributor-covenant.org) code of conduct.
108
+
109
+ ## License
110
+
111
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
112
+
113
+ ## Code of Conduct
114
+
115
+ Everyone interacting in the SimpleSauce project’s codebases, issue trackers, chat rooms and mailing lists
116
+ is expected to follow the [code of conduct](https://github.com/saucelabs/sauce_bindings/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sauce_bindings/version'
4
+ require 'sauce_bindings/options'
5
+ require 'sauce_bindings/session'
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sauce_bindings'
4
+ require 'capybara'
5
+
6
+ module SauceBindings
7
+ class CapybaraSession < Session
8
+ def initialize(*)
9
+ super
10
+
11
+ Capybara.register_driver :sauce do |app|
12
+ Capybara::Selenium::Driver.new(app, **{browser: :remote}.merge(to_selenium))
13
+ end
14
+ Capybara.current_driver = :sauce
15
+ end
16
+
17
+ def start
18
+ raise ArgumentError, "needs username; use `ENV['SAUCE_USERNAME']` or `Session#username=`" unless @username
19
+ raise ArgumentError, "needs access_key; use `ENV['SAUCE_ACCESS_KEY']` or `Session#access_key=`" unless @access_key
20
+
21
+ @capybara_driver = Capybara.current_session.driver
22
+ @driver = @capybara_driver.browser
23
+ end
24
+
25
+ def stop(result)
26
+ return if @driver.nil?
27
+
28
+ SauceWhisk::Jobs.change_status(@driver.session_id, result)
29
+
30
+ Capybara.current_session.quit
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'selenium-webdriver'
4
+
5
+ module SauceBindings
6
+ class Options
7
+ class << self
8
+ def camel_case(str)
9
+ str.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
10
+ end
11
+ end
12
+
13
+ BROWSER_NAMES = {Selenium::WebDriver::Chrome::Options => 'chrome',
14
+ Selenium::WebDriver::Edge::Options => 'MicrosoftEdge',
15
+ Selenium::WebDriver::Firefox::Options => 'firefox',
16
+ Selenium::WebDriver::IE::Options => 'internet explorer',
17
+ Selenium::WebDriver::Safari::Options => 'safari'}.freeze
18
+
19
+ W3C = %i[browser_name browser_version platform_name accept_insecure_certs page_load_strategy proxy
20
+ set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability].freeze
21
+
22
+ SAUCE = %i[avoid_proxy build chromedriver_version command_timeout custom_data extended_debugging idle_timeout
23
+ iedriver_version max_duration name parent_tunnel prerun priority public record_logs record_screenshots
24
+ record_video screen_resolution selenium_version tags time_zone tunnel_identifier video_upload_on_pass
25
+ capture_performance].freeze
26
+
27
+ attr_accessor :browser_name, :browser_version, :platform_name, :accept_insecure_certs, :page_load_strategy, :proxy,
28
+ :set_window_rect, :timeouts, :unhandled_prompt_behavior, :strict_file_interactability, :avoid_proxy,
29
+ :build, :chromedriver_version, :command_timeout, :custom_data, :extended_debugging, :idle_timeout,
30
+ :iedriver_version, :max_duration, :name, :parent_tunnel, :prerun, :priority, :public, :record_logs,
31
+ :record_screenshots, :record_video, :screen_resolution, :selenium_version, :tags, :time_zone,
32
+ :tunnel_identifier, :video_upload_on_pass, :capture_performance
33
+ attr_reader :selenium_options
34
+
35
+ def initialize(**opts)
36
+ parse_selenium_options(opts.delete(:selenium_options))
37
+ create_variables(SAUCE + W3C, opts)
38
+ @build ||= build_name
39
+
40
+ @browser_name ||= selenium_options['browserName'] || 'chrome'
41
+ @platform_name ||= 'Windows 10'
42
+ @browser_version ||= 'latest'
43
+ end
44
+
45
+ def capabilities
46
+ caps = selenium_options.dup
47
+ W3C.each do |key|
48
+ value = send(key)
49
+ key = self.class.camel_case(key)
50
+ value = parse_w3c_key(key, value)
51
+ caps[key] = value unless value.nil?
52
+ end
53
+ caps['sauce:options'] = {}
54
+ SAUCE.each do |key|
55
+ value = send(key)
56
+ key = self.class.camel_case(key)
57
+ value = parse_sauce_key(key, value)
58
+ caps['sauce:options'][key] = value unless value.nil?
59
+ end
60
+ caps
61
+ end
62
+ alias as_json capabilities
63
+
64
+ def merge_capabilities(opts)
65
+ opts.each do |key, value|
66
+ raise ArgumentError, "#{key} is not a valid parameter for Options class" unless respond_to?("#{key}=")
67
+
68
+ if value.is_a?(Hash)
69
+ value = value.each_with_object({}) do |(old_key, val), updated|
70
+ updated[old_key.to_sym] = val
71
+ end
72
+ end
73
+
74
+ send("#{key}=", value)
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def parse_w3c_key(key, value)
81
+ if key == 'proxy' && value
82
+ value.as_json
83
+ elsif key == 'timeouts' && value
84
+ value.each_with_object({}) do |(old_key, val), updated|
85
+ updated[self.class.camel_case(old_key)] = val
86
+ end
87
+ else
88
+ value
89
+ end
90
+ end
91
+
92
+ def parse_sauce_key(key, value)
93
+ if key == 'prerun' && value.is_a?(Hash)
94
+ value.each_with_object({}) do |(old_key, val), updated|
95
+ updated[self.class.camel_case(old_key)] = val
96
+ end
97
+ elsif key == 'customData' && value.is_a?(Hash)
98
+ value.each_with_object({}) do |(old_key, val), updated|
99
+ updated[self.class.camel_case(old_key)] = val
100
+ end
101
+ else
102
+ value
103
+ end
104
+ end
105
+
106
+ def parse_selenium_options(selenium_opts)
107
+ opts = Array(selenium_opts)
108
+
109
+ opts.each do |opt|
110
+ browser = BROWSER_NAMES[opt.class]
111
+ if browser
112
+ @browser_name = browser
113
+ else
114
+ W3C.each do |capability|
115
+ send("#{capability}=", opt[capability]) if opt[capability]
116
+ end
117
+ end
118
+ end
119
+
120
+ @selenium_options = opts.map(&:as_json).inject(:merge) || {}
121
+ end
122
+
123
+ def create_variables(key, opts)
124
+ key.each do |option|
125
+ self.class.__send__(:attr_accessor, option)
126
+ next unless opts.key?(option)
127
+
128
+ instance_variable_set("@#{option}", opts.delete(option))
129
+ end
130
+ return if opts.empty?
131
+
132
+ raise ArgumentError, "#{opts.inspect} are not valid parameters for Options class"
133
+ end
134
+
135
+ def build_name
136
+ # Jenkins
137
+ if ENV['BUILD_TAG']
138
+ "#{ENV['BUILD_NAME']}: #{ENV['BUILD_NUMBER']}"
139
+ # Bamboo
140
+ elsif ENV['bamboo_agentId']
141
+ "#{ENV['bamboo_shortJobName']}: #{ENV['bamboo_buildNumber']}"
142
+ # Travis
143
+ elsif ENV['TRAVIS_JOB_ID']
144
+ "#{ENV['TRAVIS_JOB_NAME']}: #{ENV['TRAVIS_JOB_NUMBER']}"
145
+ # CircleCI
146
+ elsif ENV['CIRCLE_JOB']
147
+ "#{ENV['CIRCLE_JOB']}: #{ENV['CIRCLE_BUILD_NUM']}"
148
+ # Gitlab
149
+ elsif ENV['CI']
150
+ "#{ENV['CI_JOB_NAME']}: #{ENV['CI_JOB_ID']}"
151
+ # Team City
152
+ elsif ENV['TEAMCITY_PROJECT_NAME']
153
+ "#{ENV['TEAMCITY_PROJECT_NAME']}: #{ENV['BUILD_NUMBER']}"
154
+ # Default
155
+ else
156
+ "Build Time - #{Time.now.to_i}"
157
+ end
158
+ end
159
+ end
160
+ end