atrea_control 1.0.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88aee51ff62bb07d3020bb0a65f13361ae6c36055072da6dc09d80bf4c0b2aff
4
- data.tar.gz: fd32eb80961e9355fc9bb9f9af1a9ed4b49072272b7e5142dbd5f1b17c2d4844
3
+ metadata.gz: 4a5ccc9d8596b46a4ec4c2eb98e26b820c62bfdbf91c8c1b732adc9f858f31bf
4
+ data.tar.gz: bac1c0183608fe7f16c679578203fba54c10d8e2270be7d0bf68fb1f17d7cce4
5
5
  SHA512:
6
- metadata.gz: 03aeeac63edae08f4e6ed4f7a4316846015540d3c4d5439bbb914270c66e1bf897cc399f53eb2d7f2f884c6c176198b28ce4331b37572b0abeb895fa54de5c51
7
- data.tar.gz: b9d55180b76bb447b6030f5b56cb1a617a07363ed9d239dea90264cacd450a18a7382ead94cb43de2c87fa447b02be2a0bceae3960354130ab6287a978142ec6
6
+ metadata.gz: 657e441b8aa3ecfaa5eb8905ff6e22064d70d43984156bd15c6b7dfc17f6ed92e1a7c8318cd7febeb2ce68c621ff06f69561cd8861b642f2313f4421df098597
7
+ data.tar.gz: 97dcc1fa4810f952e4b89d2f610c93a2359debb663b0138f8bf3d5771ca9e4ddfb96be2d9c8e0a0a2a2667f1daa4533606704adfc9a89571a4d0852800cf7bf1
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ .DS_Store
1
2
  /.idea
2
3
  /.bundle/
3
4
  /.yardoc
data/.rubocop_todo.yml CHANGED
@@ -1,12 +1,29 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config --auto-gen-only-exclude`
3
- # on 2021-09-13 19:43:25 UTC using RuboCop version 1.20.0.
3
+ # on 2021-10-30 20:32:55 UTC using RuboCop version 1.22.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
9
+ # Offense count: 2
10
+ # Cop supports --auto-correct.
11
+ # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator.
12
+ # SupportedStylesForExponentOperator: space, no_space
13
+ Layout/SpaceAroundOperators:
14
+ Exclude:
15
+ - '_x.rb'
16
+
17
+ # Offense count: 2
18
+ # Cop supports --auto-correct.
19
+ # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
20
+ # SupportedStyles: space, no_space, compact
21
+ # SupportedStylesForEmptyBraces: space, no_space
22
+ Layout/SpaceInsideHashLiteralBraces:
23
+ Exclude:
24
+ - '_x.rb'
25
+
26
+ # Offense count: 2
10
27
  # Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
11
28
  # IgnoredMethods: refine
12
29
  Metrics/BlockLength:
@@ -14,6 +31,12 @@ Metrics/BlockLength:
14
31
  - '**/*.gemspec'
15
32
  - 'spec/atrea_control/duplex_spec.rb'
16
33
 
34
+ # Offense count: 1
35
+ # Configuration parameters: CountComments, Max, CountAsOne.
36
+ Metrics/ClassLength:
37
+ Exclude:
38
+ - 'lib/atrea_control/duplex.rb'
39
+
17
40
  # Offense count: 4
18
41
  # Configuration parameters: .
19
42
  # SupportedStyles: have_received, receive
@@ -31,7 +54,7 @@ RSpec/StubbedMock:
31
54
  Exclude:
32
55
  - 'spec/atrea_control/duplex_spec.rb'
33
56
 
34
- # Offense count: 4
57
+ # Offense count: 6
35
58
  RSpec/SubjectStub:
36
59
  Exclude:
37
60
  - 'spec/atrea_control/duplex_spec.rb'
@@ -44,14 +67,23 @@ Style/Documentation:
44
67
  - 'test/**/*'
45
68
  - 'lib/atrea_control.rb'
46
69
 
47
- # Offense count: 1
70
+ # Offense count: 2
48
71
  # Cop supports --auto-correct.
49
72
  # Configuration parameters: EnforcedStyle.
50
73
  # SupportedStyles: always, always_true, never
51
74
  Style/FrozenStringLiteralComment:
52
75
  Exclude:
76
+ - '_x.rb'
53
77
  - 'spec/atrea_control/duplex_spec.rb'
54
78
 
79
+ # Offense count: 2
80
+ # Cop supports --auto-correct.
81
+ # Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
82
+ # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
83
+ Style/HashSyntax:
84
+ Exclude:
85
+ - '_x.rb'
86
+
55
87
  # Offense count: 1
56
88
  # Cop supports --auto-correct.
57
89
  # Configuration parameters: EnforcedStyle.
@@ -60,9 +92,9 @@ Style/MutableConstant:
60
92
  Exclude:
61
93
  - 'spec/atrea_control/duplex_spec.rb'
62
94
 
63
- # Offense count: 1
95
+ # Offense count: 2
64
96
  # Cop supports --auto-correct.
65
- # Configuration parameters: AutoCorrect, Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
97
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
66
98
  # URISchemes: http, https
67
99
  Layout/LineLength:
68
100
  Exclude:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
+ ## [1.3.0] - 2021-12-11
3
+ ### Changed
4
+ - store auth tokens outside
5
+ - minimize selenium only for login
2
6
 
3
- ## [1.0.0] - 2021-09-13
7
+ ## [1.2.1] - 2021-10-30
8
+ ### Changed
9
+ - login procedure
10
+ - ensure logged user by window.user object
11
+
12
+ ## [1.2.0] - 2021-10-26
13
+ ### Changed
14
+ - use internal unit.php for read values
15
+ - reload login automatically if session expire
16
+ ## [1.1.0] - 2021-10-22
17
+ ### Added
18
+ - to_json
4
19
 
20
+ ## [1.0.0] - 2021-09-13
5
21
  - Initial release
data/Gemfile CHANGED
@@ -5,8 +5,12 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in atrea_control.gemspec
6
6
  gemspec
7
7
 
8
+ gem "pry"
8
9
  gem "rake", "~> 13.0"
9
10
  gem "rspec", "~> 3.10"
10
11
  gem "rubocop", "~> 1.7", require: false
11
12
  gem "rubocop-rspec", "~> 2.4", require: false
12
- gem "simplecov", require: false, group: :test
13
+ group :test do
14
+ gem "simplecov", require: false
15
+ gem "webmock", "~> 3.14"
16
+ end
data/Gemfile.lock CHANGED
@@ -1,22 +1,52 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atrea_control (1.0.0)
4
+ atrea_control (1.3.0)
5
+ nokogiri (~> 1.12)
6
+ rest-client (~> 2.1)
5
7
  selenium-webdriver (~> 3.142)
6
8
 
7
9
  GEM
8
10
  remote: https://rubygems.org/
9
11
  specs:
12
+ addressable (2.8.0)
13
+ public_suffix (>= 2.0.2, < 5.0)
10
14
  ast (2.4.2)
11
15
  childprocess (3.0.0)
16
+ coderay (1.1.3)
17
+ crack (0.4.5)
18
+ rexml
12
19
  diff-lcs (1.4.4)
13
20
  docile (1.4.0)
14
- parallel (1.20.1)
21
+ domain_name (0.5.20190701)
22
+ unf (>= 0.0.5, < 1.0.0)
23
+ hashdiff (1.0.1)
24
+ http-accept (1.7.0)
25
+ http-cookie (1.0.4)
26
+ domain_name (~> 0.5)
27
+ method_source (1.0.0)
28
+ mime-types (3.4.1)
29
+ mime-types-data (~> 3.2015)
30
+ mime-types-data (3.2021.1115)
31
+ netrc (0.11.0)
32
+ nokogiri (1.12.5-x86_64-darwin)
33
+ racc (~> 1.4)
34
+ parallel (1.21.0)
15
35
  parser (3.0.2.0)
16
36
  ast (~> 2.4.1)
37
+ pry (0.14.1)
38
+ coderay (~> 1.1)
39
+ method_source (~> 1.0)
40
+ public_suffix (4.0.6)
41
+ racc (1.6.0)
17
42
  rainbow (3.0.0)
18
43
  rake (13.0.6)
19
44
  regexp_parser (2.1.1)
45
+ rest-client (2.1.0)
46
+ http-accept (>= 1.7.0, < 2.0)
47
+ http-cookie (>= 1.0.2, < 2.0)
48
+ mime-types (>= 1.16, < 4.0)
49
+ netrc (~> 0.8)
20
50
  rexml (3.2.5)
21
51
  rspec (3.10.0)
22
52
  rspec-core (~> 3.10.0)
@@ -31,20 +61,19 @@ GEM
31
61
  diff-lcs (>= 1.2.0, < 2.0)
32
62
  rspec-support (~> 3.10.0)
33
63
  rspec-support (3.10.2)
34
- rubocop (1.20.0)
64
+ rubocop (1.22.2)
35
65
  parallel (~> 1.10)
36
66
  parser (>= 3.0.0.0)
37
67
  rainbow (>= 2.2.2, < 4.0)
38
68
  regexp_parser (>= 1.8, < 3.0)
39
69
  rexml
40
- rubocop-ast (>= 1.9.1, < 2.0)
70
+ rubocop-ast (>= 1.12.0, < 2.0)
41
71
  ruby-progressbar (~> 1.7)
42
72
  unicode-display_width (>= 1.4.0, < 3.0)
43
- rubocop-ast (1.11.0)
73
+ rubocop-ast (1.12.0)
44
74
  parser (>= 3.0.1.1)
45
- rubocop-rspec (2.4.0)
46
- rubocop (~> 1.0)
47
- rubocop-ast (>= 1.1.0)
75
+ rubocop-rspec (2.5.0)
76
+ rubocop (~> 1.19)
48
77
  ruby-progressbar (1.11.0)
49
78
  rubyzip (2.3.2)
50
79
  selenium-webdriver (3.142.7)
@@ -56,7 +85,14 @@ GEM
56
85
  simplecov_json_formatter (~> 0.1)
57
86
  simplecov-html (0.12.3)
58
87
  simplecov_json_formatter (0.1.3)
59
- unicode-display_width (2.0.0)
88
+ unf (0.1.4)
89
+ unf_ext
90
+ unf_ext (0.0.8)
91
+ unicode-display_width (2.1.0)
92
+ webmock (3.14.0)
93
+ addressable (>= 2.8.0)
94
+ crack (>= 0.3.2)
95
+ hashdiff (>= 0.4.0, < 2.0.0)
60
96
 
61
97
  PLATFORMS
62
98
  x86_64-darwin-19
@@ -64,11 +100,13 @@ PLATFORMS
64
100
 
65
101
  DEPENDENCIES
66
102
  atrea_control!
103
+ pry
67
104
  rake (~> 13.0)
68
105
  rspec (~> 3.10)
69
106
  rubocop (~> 1.7)
70
107
  rubocop-rspec (~> 2.4)
71
108
  simplecov
109
+ webmock (~> 3.14)
72
110
 
73
111
  BUNDLED WITH
74
112
  2.2.17
data/README.md CHANGED
@@ -58,7 +58,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
58
58
 
59
59
  ## Contributing
60
60
 
61
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/atrea_control. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/luk4s/atrea_control/blob/master/CODE_OF_CONDUCT.md).
61
+ Bug reports and pull requests are welcome on GitHub at https://github.com/luk4s/atrea_control. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/luk4s/atrea_control/blob/master/CODE_OF_CONDUCT.md).
62
62
 
63
63
  ## Code of Conduct
64
64
 
@@ -34,5 +34,7 @@ Gem::Specification.new do |spec|
34
34
  # For more information and examples about making a new gem, checkout our
35
35
  # guide at: https://bundler.io/guides/creating_gem.html
36
36
 
37
+ spec.add_dependency "nokogiri", "~> 1.12"
38
+ spec.add_dependency "rest-client", "~> 2.1"
37
39
  spec.add_dependency "selenium-webdriver", "~> 3.142"
38
40
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "logger"
4
+ require "nokogiri"
5
+ require "rest-client"
4
6
  require "selenium-webdriver"
5
7
 
6
8
  module AtreaControl
@@ -8,7 +10,11 @@ module AtreaControl
8
10
  class Duplex
9
11
  CONTROL_URI = "https://control.atrea.eu/"
10
12
 
11
- attr_reader :driver
13
+ attr_reader :current_mode, :current_power, :outdoor_temperature
14
+ # @return [DateTime] store time of last update
15
+ attr_reader :valid_for
16
+
17
+ attr_accessor :user_id, :unit_id, :auth_token
12
18
 
13
19
  # @param [String] login
14
20
  # @param [String] password
@@ -17,23 +23,43 @@ module AtreaControl
17
23
  # @option sensors_map [String] :current_power
18
24
  # @option sensors_map [String] :current_mode
19
25
  def initialize(login:, password:, sensors_map: Config.default_sensors_map)
20
- @logged = false
21
26
  @login = login
22
27
  @password = password
23
28
  @sensors = sensors_map
29
+ end
30
+
31
+ # @return [Selenium::WebDriver::Firefox::Driver]
32
+ def driver
33
+ return @driver if defined?(@driver)
24
34
 
25
35
  options = Selenium::WebDriver::Firefox::Options.new
26
36
  options.headless! unless ENV["NO_HEADLESS"]
27
- @driver = Selenium::WebDriver.for :firefox, options: options
37
+ @driver ||= Selenium::WebDriver.for :firefox, capabilities: [options]
28
38
  end
29
39
 
30
40
  def logged?
31
- @logged
41
+ user&.[] "loged"
42
+ end
43
+
44
+ def login_in_progress?
45
+ @login_in_progress
32
46
  end
33
47
 
34
48
  # Login into control
35
49
  def login
50
+ @login_in_progress = true
51
+ logger.debug "start new login"
36
52
  driver.get CONTROL_URI
53
+ submit_login_form unless logged?
54
+ finish_login
55
+ refresh!
56
+ inspect
57
+ ensure
58
+ @login_in_progress = false
59
+ end
60
+
61
+ # Submit given credentials and proceed login
62
+ def submit_login_form
37
63
  form = driver.find_element(id: "loginFrm")
38
64
  username = form.find_element(name: "username")
39
65
  username.send_keys @login
@@ -42,87 +68,161 @@ module AtreaControl
42
68
 
43
69
  submit = form.find_element(css: "input[type=submit]")
44
70
  submit.click
45
- finish_login
46
- inspect
47
71
  end
48
72
 
49
73
  # Retrieve dashboard URI from object tag and open it again
50
74
  def open_dashboard
51
75
  uri = driver.find_element(tag_name: "object").attribute "data"
52
76
  driver.get uri
77
+ user_id && unit_id && auth_token
53
78
  logger.debug "#{name} login success"
54
- @logged = true
55
79
  end
56
80
 
57
81
  # @return [String]
58
82
  def name
59
- return unless logged?
83
+ @name ||= driver.find_element(css: "div#pageTitle > h2")&.text if logged?
84
+ @name
85
+ end
60
86
 
61
- container = driver.find_element css: "div#pageTitle > h2"
62
- container.text
87
+ # # @return [String] ID of logged user
88
+ # def user_id
89
+ # @user_id ||= driver.execute_script("return window._user")
90
+ # end
91
+ #
92
+ # # @return [String] ID of recuperation unit
93
+ # def unit_id
94
+ # @unit_id ||= driver.execute_script("return window._unit")
95
+ # end
96
+ #
97
+ # # @return [String] session token
98
+ # def auth_token
99
+ # @auth_token ||= user&.[]("auth")
100
+ # end
101
+
102
+ # Window.user object from atrea
103
+ # @return [Hash, nil]
104
+ def user
105
+ driver.execute_script("return window.user")
63
106
  end
64
107
 
65
- def unit_id
66
- return unless logged?
108
+ # @return [String]
109
+ def current_mode_name
110
+ return current_mode unless logged?
111
+
112
+ element = sensor_element(@sensors[:current_mode])
113
+ element.find_element(css: "div:first-child").text
114
+ end
67
115
 
68
- element = driver.find_element css: "div#pageTitle > a"
69
- element.attribute(:href) =~ /unit=(\d+)/
70
- Regexp.last_match(1)
116
+ # quit selenium browser
117
+ def close
118
+ driver.quit
119
+ ensure
120
+ remove_instance_variable :@driver
71
121
  end
72
122
 
73
- # @return [Float]
74
- def outdoor_temperature
75
- return unless logged?
123
+ alias logout! close
76
124
 
77
- element = sensor_element(@sensors[__method__])
78
- element.find_element(css: "div").text.to_f
125
+ def as_json(_options = nil)
126
+ {
127
+ current_mode: current_mode,
128
+ current_power: current_power,
129
+ outdoor_temperature: outdoor_temperature,
130
+ valid_for: valid_for,
131
+ }
79
132
  end
80
133
 
81
- # @return [Float] in %
82
- def current_power
83
- return unless logged?
134
+ alias values as_json
84
135
 
85
- # element = driver.find_element id: "contentBox#{@sensors[__method__]}"
86
- element = sensor_element(@sensors[__method__])
87
- element.find_element(css: "div:first-child").text.to_f
136
+ def to_json(*args)
137
+ as_json.to_json(*args)
88
138
  end
89
139
 
90
- # @return [String]
91
- def current_mode
92
- return unless logged?
140
+ # def inspect
141
+ # "<AtreaControl name: '#{name}' outdoor_temperature: '#{outdoor_temperature}°C' current_power: '#{current_power}%' current_mode: '#{current_mode}' valid_for: '#{valid_for}'>"
142
+ # end
93
143
 
94
- element = sensor_element(@sensors[__method__])
95
- element.find_element(css: "div:first-child").text
144
+ def call_unit!
145
+ return false if @login_in_progress
146
+
147
+ logger.debug "call_unit!"
148
+ parse_response(response_comm_unit)
149
+ @valid_for = Time.now
150
+ as_json
96
151
  end
97
152
 
98
- def inspect
99
- "<AtreaControl name: '#{name}' outdoor_temperature: #{outdoor_temperature}°C current_power: #{current_power}% current_mode: '#{current_mode}>'"
153
+ private
154
+
155
+ # @see scripts.php -> loadRD5Values(node, init)
156
+ # @note
157
+ # if(values[key]>32767) values[key]-=65536;
158
+ # if(params[key] && params[key].offset)
159
+ # values[key]=values[key]-params[key].offset;
160
+ # if(params[key] && params[key].coef)
161
+ # values[key]=values[key]/params[key].coef;
162
+ def parse_response(response)
163
+ xml = Nokogiri::XML response.body
164
+ sensors_values = @sensors.transform_values do |id|
165
+ value = xml.xpath("//O[@I=\"#{id}\"]/@V").last&.value.to_i
166
+ value -= 65_536 if value > 32_767
167
+ # value -= 0 if "offset"
168
+ # value -= 0 if "coef"
169
+ value
170
+ end
171
+ refresh_data(sensors_values)
100
172
  end
101
173
 
102
- def close
103
- @logged = false
104
- driver.quit
174
+ # @param [Hash] values
175
+ # @return [Hash]
176
+ def refresh_data(values)
177
+ @outdoor_temperature = values[:outdoor_temperature].to_f / 10.0
178
+ @current_power = values[:current_power].to_f
179
+ @current_mode = mode_map[values[:current_mode]]
180
+
181
+ as_json
105
182
  end
106
183
 
107
- private
184
+ # ? I10204 ?
185
+ def mode_map
186
+ { 0 => "Vypnuto", 1 => "Automat", 2 => "Větrání", 6 => "Rozvážení" }
187
+ end
108
188
 
109
189
  def logger
110
190
  @logger ||= ::Logger.new($stdout)
111
191
  end
112
192
 
113
- def sensor_element(sensor_id)
114
- driver.find_element id: "contentBox#{sensor_id}"
115
- end
116
-
117
193
  def finish_login
118
- 8.times do |i|
194
+ 13.times do |i|
119
195
  return true if open_dashboard
120
196
  rescue Selenium::WebDriver::Error::NoSuchElementError => _e
121
- t = 5 * (1 + i)
197
+ t = [3 * (1 + i), 25].min
122
198
  logger.debug "waiting #{t}s for login..."
123
199
  sleep t
124
200
  end
125
201
  raise Error, "unable to login"
126
202
  end
203
+
204
+ def sensor_element(sensor_id)
205
+ driver.find_element id: "contentBox#{sensor_id}"
206
+ end
207
+
208
+ # @return [RestClient::Response]
209
+ def response_comm_unit
210
+ params = {
211
+ _user: user_id.to_i,
212
+ _unit: unit_id,
213
+ auth: auth_token || "null",
214
+ _t: "config/xml.xml",
215
+ }
216
+ autologin_token = CGI.escape([@login, @password].join("\b"))
217
+ RestClient.get "https://control.atrea.eu/comm/sw/unit.php", { Cookie: "autoLogin=#{autologin_token}", params: params }
218
+ end
219
+
220
+ # Update tokens based on current state
221
+ def refresh!
222
+ @user_id = driver.execute_script("return window._user")
223
+ @unit_id = driver.execute_script("return window._unit")
224
+ @auth_token = user&.[]("auth")
225
+ end
226
+
127
227
  end
128
228
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtreaControl
4
- VERSION = "1.0.0"
4
+
5
+ VERSION = "1.3.0"
6
+
5
7
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atrea_control
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lukáš Pokorný
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-16 00:00:00.000000000 Z
11
+ date: 2021-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.1'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: selenium-webdriver
15
43
  requirement: !ruby/object:Gem::Requirement