unobtainium 0.1.1 → 0.2.0

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