unobtainium 0.1.1 → 0.2.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
  SHA1:
3
- metadata.gz: c5ce28fc39781ca3d3ad6b7116e93f3b194e44d2
4
- data.tar.gz: 58b285e599f453331bfe7c7382158fa09d591f1e
3
+ metadata.gz: 62f55e229f2458af4e265380afd8e407135e2f6e
4
+ data.tar.gz: 84c5321fff693ec7eedddc59f74bbe829303f383
5
5
  SHA512:
6
- metadata.gz: 8e8d24aae8e1143ca3ec5a7d6043968f6c0bc6f1ced20bd35f868bf2c5ca74cc669caada417a58d23509987bdeca92e18abcc92abaa78b37d02cd936c549569f
7
- data.tar.gz: 4ee5af3d346f55488f65f33929a37cc18a39c6ba42337db2ad25c94fc695d133f72975566d3c9804c5f584dd3d3a5460ce661782db5f3b7a5f72d54925dcabd5
6
+ metadata.gz: 070ff928ed2373b5f26bcb9d4c596d6d7ced6443d0028feeaba897a1cb66accec60b8612204245ef9c76dc00213bab8c0c9ea0562483d286d7d3524926536781
7
+ data.tar.gz: 2c8b4a547d1cc410d60f0d0d86c214720097f1da2ea7a4d1717d7b1645615be12add8ccfac7eac53f1e091e4c8ef32ef28b8672f1366d7db87ebffad32acf4a5
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- unobtainium (0.1.0)
4
+ unobtainium (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/cuke/Gemfile.lock ADDED
@@ -0,0 +1,59 @@
1
+ GIT
2
+ remote: git@github.com:jfinkhaeuser/unobtainium.git
3
+ revision: 16b26ed3c3f60bd114ab774a717d3050feb94e03
4
+ branch: master
5
+ specs:
6
+ unobtainium (0.2.0)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ appium_lib (8.0.2)
12
+ awesome_print (~> 1.6)
13
+ json (~> 1.8)
14
+ nokogiri (~> 1.6.6)
15
+ selenium-webdriver (~> 2.49)
16
+ tomlrb (~> 1.1)
17
+ awesome_print (1.6.1)
18
+ builder (3.2.2)
19
+ childprocess (0.5.9)
20
+ ffi (~> 1.0, >= 1.0.11)
21
+ cucumber (2.3.3)
22
+ builder (>= 2.1.2)
23
+ cucumber-core (~> 1.4.0)
24
+ cucumber-wire (~> 0.0.1)
25
+ diff-lcs (>= 1.1.3)
26
+ gherkin (~> 3.2.0)
27
+ multi_json (>= 1.7.5, < 2.0)
28
+ multi_test (>= 0.1.2)
29
+ cucumber-core (1.4.0)
30
+ gherkin (~> 3.2.0)
31
+ cucumber-wire (0.0.1)
32
+ diff-lcs (1.2.5)
33
+ ffi (1.9.10)
34
+ gherkin (3.2.0)
35
+ json (1.8.3)
36
+ mini_portile2 (2.0.0)
37
+ multi_json (1.11.2)
38
+ multi_test (0.1.2)
39
+ nokogiri (1.6.7.2)
40
+ mini_portile2 (~> 2.0.0.rc2)
41
+ rubyzip (1.2.0)
42
+ selenium-webdriver (2.53.0)
43
+ childprocess (~> 0.5)
44
+ rubyzip (~> 1.0)
45
+ websocket (~> 1.0)
46
+ tomlrb (1.2.1)
47
+ websocket (1.2.3)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ appium_lib
54
+ cucumber
55
+ selenium-webdriver
56
+ unobtainium!
57
+
58
+ BUNDLED WITH
59
+ 1.11.2
@@ -4,3 +4,4 @@ drivers:
4
4
  android:
5
5
  browser: chrome
6
6
  driver: firefox
7
+ at_end: quit
@@ -59,14 +59,23 @@ module Unobtainium
59
59
  # Wrap all read functions into something that checks for environment
60
60
  # variables first.
61
61
  define_method(method) do |*args, &block|
62
+ # If there are no arguments, there's nothing to do with paths. Just
63
+ # delegate to the hash.
64
+ if args.empty?
65
+ return super(*args, &block)
66
+ end
67
+
62
68
  # We'll make it rather simple: since the first argument is a key, we
63
69
  # will just transform it to the matching environment variable name,
64
70
  # and see if that environment variable is set.
65
- env_name = args[0].upcase.gsub(split_pattern, '_')
66
- contents = ENV[env_name]
71
+ env_name = args[0].to_s.upcase.gsub(split_pattern, '_')
72
+ contents = nil
73
+ if env_name != '_'
74
+ contents = ENV[env_name]
75
+ end
67
76
 
68
77
  # No environment variable set? Fine, just do the usual thing.
69
- if contents.nil?
78
+ if contents.nil? or contents.empty?
70
79
  return super(*args, &block)
71
80
  end
72
81
 
@@ -94,28 +103,23 @@ module Unobtainium
94
103
  ##
95
104
  # Loads a configuration file with the given file name. The format is
96
105
  # detected based on one of the extensions in FILE_TO_PARSER.
97
- def load_config(path)
106
+ def load_config(path, resolve_extensions = true)
98
107
  # Load base and local configuration files
99
108
  base, config = load_base_config(path)
100
109
  _, local_config = load_local_config(base)
101
- if local_config.nil?
102
- return Config.new(config)
103
- end
104
110
 
105
- # Merge
106
- merger = proc do |_, v1, v2|
107
- # rubocop:disable Style/GuardClause
108
- if v1.is_a? Hash and v2.is_a? Hash
109
- next v1.merge(v2, &merger)
110
- elsif v1.is_a? Array and v2.is_a? Array
111
- next v1 + v2
112
- end
113
- next v2
114
- # rubocop:enable Style/GuardClause
111
+ # Merge local configuration
112
+ config.recursive_merge!(local_config)
113
+
114
+ # Create config from the result
115
+ cfg = Config.new(config)
116
+
117
+ # Now resolve config hashes that extend other hashes.
118
+ if resolve_extensions
119
+ cfg.resolve_extensions!
115
120
  end
116
- config.merge!(local_config, &merger)
117
121
 
118
- return Config.new(config)
122
+ return cfg
119
123
  end
120
124
 
121
125
  private
@@ -137,7 +141,7 @@ module Unobtainium
137
141
  # Parse the contents.
138
142
  config = FILE_TO_PARSER[base.extname].parse(contents)
139
143
 
140
- return base, hashify(config)
144
+ return base, PathedHash.new(hashify(config))
141
145
  end
142
146
 
143
147
  def load_local_config(base)
@@ -156,7 +160,7 @@ module Unobtainium
156
160
 
157
161
  local_config = FILE_TO_PARSER[base.extname].parse(contents)
158
162
 
159
- return local, hashify(local_config)
163
+ return local, PathedHash.new(hashify(local_config))
160
164
  end
161
165
 
162
166
  def hashify(data)
@@ -169,5 +173,111 @@ module Unobtainium
169
173
  return data
170
174
  end
171
175
  end # class << self
176
+
177
+ ##
178
+ # Resolve extensions in configuration hashes. If your hash contains e.g.:
179
+ #
180
+ # foo:
181
+ # bar:
182
+ # some: value
183
+ # baz:
184
+ # extends: bar
185
+ #
186
+ # Then 'foo.baz.some' will equal 'value' after resolving extensions. Note
187
+ # that :load_config calls this function, so normally you don't need to call
188
+ # it yourself. You can switch this behaviour off in :load_config.
189
+ #
190
+ # Note that this process has some intended side-effects:
191
+ # 1) If a hash can't be extended because the base cannot be found, an error
192
+ # is raised.
193
+ # 2) If a hash got successfully extended, the :extends keyword itself is
194
+ # removed from the hash.
195
+ # 3) In a successfully extended hash, an :base keyword, which contains
196
+ # the name of the base. In case of multiple recursive extensions, the
197
+ # final base is stored here.
198
+ #
199
+ # Also note that all of this means that :extends and :base are reserved
200
+ # keywords that cannot be used in configuration files other than for this
201
+ # purpose!
202
+ def resolve_extensions!
203
+ recursive_merge("", "")
204
+ end
205
+
206
+ def resolve_extensions
207
+ dup.resolve_extensions!
208
+ end
209
+
210
+ private
211
+
212
+ def recursive_merge(parent, key)
213
+ loop do
214
+ full_key = "#{parent}#{separator}#{key}"
215
+
216
+ # Recurse down to the remaining root of the hierarchy
217
+ base = full_key
218
+ derived = nil
219
+ loop do
220
+ new_base, new_derived = resolve_extension(parent, base)
221
+
222
+ if new_derived.nil?
223
+ break
224
+ end
225
+
226
+ base = new_base
227
+ derived = new_derived
228
+ end
229
+
230
+ # If recursion found nothing to merge, we're done!
231
+ if derived.nil?
232
+ break
233
+ end
234
+
235
+ # Otherwise, merge what needs merging and continue
236
+ merge_extension(base, derived)
237
+ end
238
+ end
239
+
240
+ def resolve_extension(grandparent, parent)
241
+ fetch(parent, {}).each do |key, value|
242
+ # Recurse into hash values
243
+ if value.is_a? Hash
244
+ recursive_merge(parent, key)
245
+ end
246
+
247
+ # No hash, ignore any keys other than the special "extends" key
248
+ if key != "extends"
249
+ next
250
+ end
251
+
252
+ # If the key is "extends", return a normalized version of its value.
253
+ full_value = value.dup
254
+ if not full_value.start_with?(separator)
255
+ full_value = "#{grandparent}#{separator}#{value}"
256
+ end
257
+
258
+ if full_value == parent
259
+ next
260
+ end
261
+ return full_value, parent
262
+ end
263
+
264
+ return nil, nil
265
+ end
266
+
267
+ def merge_extension(base, derived)
268
+ # Remove old 'extends' key, but remember the value
269
+ extends = self[derived]["extends"]
270
+ self[derived].delete("extends")
271
+
272
+ # Recursively merge base into derived without overwriting
273
+ self[derived].extend(::Unobtainium::RecursiveMerge)
274
+ self[derived].recursive_merge!(self[base], false)
275
+
276
+ # Then set the "base" keyword, but only if it's not yet set.
277
+ if not self[derived]["base"].nil?
278
+ return
279
+ end
280
+ self[derived]["base"] = extends
281
+ end
172
282
  end # class Config
173
283
  end # module Unobtainium
@@ -19,6 +19,7 @@ module Unobtainium
19
19
  safari: [],
20
20
  chrome: [],
21
21
  chromium: [],
22
+ remote: [],
22
23
  }.freeze
23
24
 
24
25
  class << self
@@ -39,10 +40,26 @@ module Unobtainium
39
40
  err.backtrace
40
41
  end
41
42
 
43
+ ##
44
+ # Selenium really wants symbol keys for the options
45
+ def sanitize_options(label, options)
46
+ new_opts = {}
47
+
48
+ if not options.nil?
49
+ options.each do |key, value|
50
+ new_opts[key.to_sym] = value
51
+ end
52
+ end
53
+
54
+ options = new_opts
55
+
56
+ return label, options
57
+ end
58
+
42
59
  ##
43
60
  # Create and return a driver instance
44
- def create(label, _)
45
- driver = ::Selenium::WebDriver.for(normalize_label(label))
61
+ def create(label, options)
62
+ driver = ::Selenium::WebDriver.for(normalize_label(label), options)
46
63
  return driver
47
64
  end
48
65
 
@@ -7,6 +7,8 @@
7
7
  # All rights reserved.
8
8
  #
9
9
 
10
+ require 'unobtainium/recursive_merge'
11
+
10
12
  module Unobtainium
11
13
 
12
14
  ##
@@ -14,10 +16,16 @@ module Unobtainium
14
16
  # regular access, i.e. instead of h["first"]["second"] you can write
15
17
  # h["first.second"]
16
18
  class PathedHash
19
+ include RecursiveMerge
20
+
17
21
  ##
18
22
  # Initializer
19
23
  def initialize(init = {})
20
- @data = init
24
+ if init.nil?
25
+ @data = {}
26
+ else
27
+ @data = init.dup
28
+ end
21
29
  @separator = '.'
22
30
  end
23
31
 
@@ -40,13 +48,39 @@ module Unobtainium
40
48
  (READ_METHODS + WRITE_METHODS).each do |method|
41
49
  # Wrap all accessor functions to deal with paths
42
50
  define_method(method) do |*args, &block|
51
+ # If there are no arguments, there's nothing to do with paths. Just
52
+ # delegate to the hash.
53
+ if args.empty?
54
+ return @data.send(method, *args, &block)
55
+ end
56
+
43
57
  # With any of the dispatch methods, we know that the first argument has
44
58
  # to be a key. We'll try to split it by the path separator.
45
59
  components = args[0].to_s.split(split_pattern)
60
+ loop do
61
+ if components.empty? or not components[0].empty?
62
+ break
63
+ end
64
+ components.shift
65
+ end
66
+
67
+ # If there are no components, return self/the root
68
+ if components.empty?
69
+ return self
70
+ end
46
71
 
47
72
  # This PathedHash is already the leaf-most Hash
48
73
  if components.length == 1
49
- return @data.send(method, *args, &block)
74
+ # Weird edge case: if we didn't have to shift anything, then it's
75
+ # possible we inadvertently changed a symbol key into a string key,
76
+ # which could mean looking fails.
77
+ # We can detect that by comparing copy[0] to a symbolized version of
78
+ # components[0].
79
+ copy = args.dup
80
+ if copy[0] != components[0].to_sym
81
+ copy[0] = components[0]
82
+ end
83
+ return @data.send(method, *copy, &block)
50
84
  end
51
85
 
52
86
  # Deal with other paths. The frustrating part here is that for nested
@@ -57,13 +91,6 @@ module Unobtainium
57
91
  leaf = recursive_fetch(components, @data,
58
92
  create: WRITE_METHODS.include?(method))
59
93
 
60
- # If the leaf is nil, we can't send it any method without raising
61
- # an error. We'll instead send the method to an empty hash, to mimic
62
- # the correct behaviour.
63
- if leaf.nil?
64
- return {}.send(method, *args, &block)
65
- end
66
-
67
94
  # If we have a leaf, we want to send the requested method to that
68
95
  # leaf.
69
96
  copy = args.dup
@@ -76,6 +103,16 @@ module Unobtainium
76
103
  @data.to_s
77
104
  end
78
105
 
106
+ def dup
107
+ PathedHash.new(@data.dup)
108
+ end
109
+
110
+ def merge!(*args, &block)
111
+ # FIXME: we may need other methods like this. This is used by
112
+ # RecursiveMerge, so we know it's required.
113
+ PathedHash.new(super)
114
+ end
115
+
79
116
  ##
80
117
  # Map any missing method to the driver implementation
81
118
  def respond_to?(meth)
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ #
3
+ # unobtainium
4
+ # https://github.com/jfinkhaeuser/unobtainium
5
+ #
6
+ # Copyright (c) 2016 Jens Finkhaeuser and other unobtainium contributors.
7
+ # All rights reserved.
8
+ #
9
+
10
+ module Unobtainium
11
+ ##
12
+ # Provides recursive merge functions for hashes. Used in PathedHash.
13
+ module RecursiveMerge
14
+ def recursive_merge!(other, overwrite = true)
15
+ if other.nil?
16
+ return self
17
+ end
18
+
19
+ merger = proc do |_, v1, v2|
20
+ # rubocop:disable Style/GuardClause
21
+ if v1.is_a? Hash and v2.is_a? Hash
22
+ next v1.merge(v2, &merger)
23
+ elsif v1.is_a? Array and v2.is_a? Array
24
+ next v1 + v2
25
+ end
26
+ if overwrite
27
+ next v2
28
+ else
29
+ next v1
30
+ end
31
+ # rubocop:enable Style/GuardClause
32
+ end
33
+ merge!(other, &merger)
34
+ end
35
+
36
+ def recursive_merge(other, overwrite = true)
37
+ dup.recursive_merge!(other, overwrite)
38
+ end
39
+ end # module RecursiveMerge
40
+ end # module Unobtainium
@@ -7,5 +7,5 @@
7
7
  # All rights reserved.
8
8
  #
9
9
  module Unobtainium
10
- VERSION = "0.1.1".freeze
10
+ VERSION = "0.2.0".freeze
11
11
  end
@@ -53,6 +53,12 @@ module Unobtainium
53
53
  options = config["drivers.#{label}"]
54
54
  end
55
55
 
56
+ # The merged/extended options might define a "base"; that's the label
57
+ # we need to use.
58
+ if not options.nil? and not options["base"].nil?
59
+ label = options["base"]
60
+ end
61
+
56
62
  # The driver may modify the options; if so, we should let it do that
57
63
  # here. That way our key (below) is based on the expanded options.
58
64
  label, options = ::Unobtainium::Driver.sanitize_options(label, options)
@@ -64,19 +70,21 @@ module Unobtainium
64
70
  key = Digest::SHA1.hexdigest(key.to_s)
65
71
  key = "driver-#{key}"
66
72
 
67
- # Only create a driver with this exact configuration once
68
- dtor = ::Unobtainium::World.method(:driver_destructor)
69
- return ::Unobtainium::Runtime.instance.store_with_if(key, dtor) do
70
- ::Unobtainium::Driver.create(label, options)
71
- end
72
- end
73
-
74
- class << self
75
- def driver_destructor(the_driver = nil)
73
+ # Only create a driver with this exact configuration once. Unfortunately
74
+ # We'll have to bind the destructor to whatever configuration exists at
75
+ # this point in time, so we have to create a proc here - whether the Driver
76
+ # gets created or not.
77
+ at_end = config.fetch("at_end", "quit")
78
+ dtor = proc do |the_driver|
76
79
  if the_driver.nil?
77
80
  return
78
81
  end
79
- the_driver.close
82
+
83
+ meth = at_end.to_sym
84
+ the_driver.send(meth)
85
+ end
86
+ return ::Unobtainium::Runtime.instance.store_with_if(key, dtor) do
87
+ ::Unobtainium::Driver.create(label, options)
80
88
  end
81
89
  end
82
90
  end # module World
data/spec/config_spec.rb CHANGED
@@ -79,4 +79,30 @@ describe ::Unobtainium::Config do
79
79
  cfg = ::Unobtainium::Config.load_config(config)
80
80
  expect(cfg).to be_empty
81
81
  end
82
+
83
+ it "extends configuration hashes" do
84
+ config = File.join(@data_path, 'driverconfig.yml')
85
+ cfg = ::Unobtainium::Config.load_config(config)
86
+
87
+ # First, test for non-extended values
88
+ expect(cfg["drivers.mock.mockoption"]).to eql 42
89
+ expect(cfg["drivers.branch1.branch1option"]).to eql "foo"
90
+ expect(cfg["drivers.branch2.branch2option"]).to eql "bar"
91
+ expect(cfg["drivers.leaf.leafoption"]).to eql "baz"
92
+
93
+ # Now test extended values
94
+ expect(cfg["drivers.branch1.mockoption"]).to eql 42
95
+ expect(cfg["drivers.branch2.mockoption"]).to eql 42
96
+ expect(cfg["drivers.leaf.mockoption"]).to eql 42
97
+
98
+ expect(cfg["drivers.branch2.branch1option"]).to eql "foo"
99
+ expect(cfg["drivers.leaf.branch1option"]).to eql "override" # not "foo" !
100
+
101
+ expect(cfg["drivers.leaf.branch2option"]).to eql "bar"
102
+
103
+ # Also test that all levels go back to base == mock
104
+ expect(cfg["drivers.branch1.base"]).to eql 'mock'
105
+ expect(cfg["drivers.branch2.base"]).to eql 'mock'
106
+ expect(cfg["drivers.leaf.base"]).to eql 'mock'
107
+ end
82
108
  end
@@ -0,0 +1,23 @@
1
+ # the hierarchy is mock -> branch1 -> branch2 -> leaf
2
+ #
3
+ # We test
4
+ # a) having a base before its derived by ordering mock before branch1
5
+ # b) having a base after its derived by ordering branch2 after leaf
6
+ # c) overrides by having leaf override the option from branch1
7
+ #
8
+ # so the order in this file has to be mock -> branch2 -> branch1 -> leaf
9
+ ---
10
+ drivers:
11
+ mock:
12
+ mockoption: 42
13
+ branch2:
14
+ extends: branch1
15
+ branch2option: bar
16
+ branch1:
17
+ extends: mock
18
+ branch1option: foo
19
+ leaf:
20
+ extends: branch2
21
+ leafoption: baz
22
+ branch1option: override
23
+ driver: leaf
@@ -13,6 +13,11 @@ describe ::Unobtainium::PathedHash do
13
13
  expect(ph.empty?).to eql false
14
14
  expect(ph[:foo]).to eql 42
15
15
  end
16
+
17
+ it "can be constructed with a nil value" do
18
+ ph = ::Unobtainium::PathedHash.new(nil)
19
+ expect(ph.empty?).to eql true
20
+ end
16
21
  end
17
22
 
18
23
  describe "Hash-like" do
@@ -49,6 +54,20 @@ describe ::Unobtainium::PathedHash do
49
54
  expect(ph["bar.nope"]).to eql nil
50
55
  end
51
56
 
57
+ it "treats a single separator as the root" do
58
+ sample = { "foo" => 42 }
59
+ ph = ::Unobtainium::PathedHash.new(sample)
60
+
61
+ expect(ph[ph.separator]["foo"]).to eql 42
62
+ end
63
+
64
+ it "treats an empty path as the root" do
65
+ sample = { "foo" => 42 }
66
+ ph = ::Unobtainium::PathedHash.new(sample)
67
+
68
+ expect(ph[""]["foo"]).to eql 42
69
+ end
70
+
52
71
  it "can recursively write entries via a path" do
53
72
  ph = ::Unobtainium::PathedHash.new
54
73
  ph["foo.bar"] = 42
@@ -60,4 +79,58 @@ describe ::Unobtainium::PathedHash do
60
79
  ph = ::Unobtainium::PathedHash.new(h)
61
80
  expect(ph.to_s).to eql h.to_s
62
81
  end
82
+
83
+ it "understands absolute paths (starting with separator)" do
84
+ sample = {
85
+ "foo" => 42,
86
+ "bar" => {
87
+ "baz" => "quux",
88
+ "blah" => [1, 2],
89
+ }
90
+ }
91
+ ph = ::Unobtainium::PathedHash.new(sample)
92
+
93
+ expect(ph["bar.baz"]).to eql "quux"
94
+ expect(ph[".bar.baz"]).to eql "quux"
95
+ end
96
+
97
+ it "recursively merges with overwriting" do
98
+ sample1 = {
99
+ "foo" => {
100
+ "bar" => 42,
101
+ "baz" => "quux",
102
+ }
103
+ }
104
+ sample2 = {
105
+ "foo" => {
106
+ "baz" => "override"
107
+ }
108
+ }
109
+
110
+ ph1 = ::Unobtainium::PathedHash.new(sample1)
111
+ ph2 = ph1.recursive_merge(sample2)
112
+
113
+ expect(ph2["foo.bar"]).to eql 42
114
+ expect(ph2["foo.baz"]).to eql "override"
115
+ end
116
+
117
+ it "recursively merges without overwriting" do
118
+ sample1 = {
119
+ "foo" => {
120
+ "bar" => 42,
121
+ "baz" => "quux",
122
+ }
123
+ }
124
+ sample2 = {
125
+ "foo" => {
126
+ "baz" => "override"
127
+ }
128
+ }
129
+
130
+ ph1 = ::Unobtainium::PathedHash.new(sample1)
131
+ ph2 = ph1.recursive_merge(sample2, false)
132
+
133
+ expect(ph2["foo.bar"]).to eql 42
134
+ expect(ph2["foo.baz"]).to eql "quux"
135
+ end
63
136
  end
data/spec/world_spec.rb CHANGED
@@ -9,7 +9,7 @@ end # class Tester
9
9
  describe ::Unobtainium::World do
10
10
  before :each do
11
11
  # Set configuration
12
- path = File.join(File.dirname(__FILE__), 'data', 'world.yml')
12
+ path = File.join(File.dirname(__FILE__), 'data', 'driverconfig.yml')
13
13
  ::Unobtainium::World.config_file = path
14
14
 
15
15
  # Load MockDriver
@@ -20,7 +20,7 @@ describe ::Unobtainium::World do
20
20
  end
21
21
 
22
22
  it "loads the global config" do
23
- expect(@tester.config["drivers.mock.option"]).to eql "value"
23
+ expect(@tester.config["drivers.mock.mockoption"]).to eql 42
24
24
  end
25
25
 
26
26
  it "creates a mock driver parameters" do
@@ -28,6 +28,10 @@ describe ::Unobtainium::World do
28
28
  end
29
29
 
30
30
  it "passed the config file options to the driver" do
31
- expect(@tester.driver.passed_options["option"]).to eql "value"
31
+ expect(@tester.driver.passed_options["mockoption"]).to eql 42
32
+ end
33
+
34
+ it "extends driver options" do
35
+ expect(@tester.driver.passed_options["base"]).to eql "mock"
32
36
  end
33
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unobtainium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Finkhaeuser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-12 00:00:00.000000000 Z
11
+ date: 2016-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -101,6 +101,7 @@ files:
101
101
  - Rakefile
102
102
  - cuke/.gitignore
103
103
  - cuke/Gemfile
104
+ - cuke/Gemfile.lock
104
105
  - cuke/README.md
105
106
  - cuke/config/config.yml
106
107
  - cuke/features/step_definitions/steps.rb
@@ -112,6 +113,7 @@ files:
112
113
  - lib/unobtainium/drivers/appium.rb
113
114
  - lib/unobtainium/drivers/selenium.rb
114
115
  - lib/unobtainium/pathed_hash.rb
116
+ - lib/unobtainium/recursive_merge.rb
115
117
  - lib/unobtainium/runtime.rb
116
118
  - lib/unobtainium/version.rb
117
119
  - lib/unobtainium/world.rb
@@ -119,6 +121,7 @@ files:
119
121
  - spec/data/array.yaml
120
122
  - spec/data/arraymerge-local.yaml
121
123
  - spec/data/arraymerge.yaml
124
+ - spec/data/driverconfig.yml
122
125
  - spec/data/empty.yml
123
126
  - spec/data/hash.yml
124
127
  - spec/data/hashmerge-local.yml
@@ -164,6 +167,7 @@ test_files:
164
167
  - spec/data/array.yaml
165
168
  - spec/data/arraymerge-local.yaml
166
169
  - spec/data/arraymerge.yaml
170
+ - spec/data/driverconfig.yml
167
171
  - spec/data/empty.yml
168
172
  - spec/data/hash.yml
169
173
  - spec/data/hashmerge-local.yml