unobtainium 0.3.0 → 0.3.1
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 +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile.lock +4 -4
- data/LICENSE +1 -1
- data/README.md +10 -1
- data/docs/CONFIGURATION.md +3 -1
- data/docs/DRIVERS.md +102 -0
- data/lib/unobtainium/pathed_hash.rb +14 -0
- data/lib/unobtainium/version.rb +1 -1
- data/lib/unobtainium/world.rb +5 -1
- data/spec/pathed_hash_spec.rb +25 -0
- data/spec/spec_helper.rb +7 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aa9b74f8f1f3c7d408438a0cff087c268dc89d4
|
4
|
+
data.tar.gz: 31f3f35f5ff7b1eccf2fd121c233b62d0056c386
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 953d864dd1286c499979044503a8655426f9e14912878d8ebd9300a3ddc9492143c8efe2f014168a44fe7131286107ba318c3c0509591832fda7fd67611dd667
|
7
|
+
data.tar.gz: a5bfa05e01c37fd7a8f6a6e15d05227f9552971d708d65fa4a54a290d742d50f0152f95ad55cf9dc51bd6bcf5ee7c44cfc2574caf85cf32069cda8f20ed2fe04
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
unobtainium (0.
|
4
|
+
unobtainium (0.3.1)
|
5
5
|
sys-proctable (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -37,11 +37,11 @@ GEM
|
|
37
37
|
gherkin (3.2.0)
|
38
38
|
json (1.8.3)
|
39
39
|
mini_portile2 (2.0.0)
|
40
|
-
multi_json (1.11.
|
40
|
+
multi_json (1.11.3)
|
41
41
|
multi_test (0.1.2)
|
42
42
|
nokogiri (1.6.7.2)
|
43
43
|
mini_portile2 (~> 2.0.0.rc2)
|
44
|
-
parser (2.3.0
|
44
|
+
parser (2.3.1.0)
|
45
45
|
ast (~> 2.2)
|
46
46
|
phantomjs (2.1.1.0)
|
47
47
|
powerpack (0.1.1)
|
@@ -66,7 +66,7 @@ GEM
|
|
66
66
|
rainbow (>= 1.99.1, < 3.0)
|
67
67
|
ruby-progressbar (~> 1.7)
|
68
68
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
69
|
-
ruby-progressbar (1.
|
69
|
+
ruby-progressbar (1.8.0)
|
70
70
|
rubyzip (1.2.0)
|
71
71
|
selenium-webdriver (2.53.0)
|
72
72
|
childprocess (~> 0.5)
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -24,6 +24,8 @@ also added.
|
|
24
24
|
You can use unobtainium on its own, or use it as part of a
|
25
25
|
[cucumber](https://cucumber.io/) test suite.
|
26
26
|
|
27
|
+
[](http://www.youtube.com/watch?v=82pYWG5uTnM)
|
28
|
+
|
27
29
|
Unobtainium's functionality is in standalone classes, but it's all combined in
|
28
30
|
the `Unobtainium::World` module.
|
29
31
|
|
@@ -36,7 +38,7 @@ the `Unobtainium::World` module.
|
|
36
38
|
|
37
39
|
- The `Config` class is a `PathedHash`, but also reads JSON or YAML files to
|
38
40
|
initialize itself with values. See the documentation on [configuration features](docs/CONFIGURATION.md)
|
39
|
-
for details
|
41
|
+
for details.
|
40
42
|
- The `Runtime` class is a singleton and a `Hash`-like container (but simpler),
|
41
43
|
that destroys all of its contents at the end of a script, calling custom
|
42
44
|
destructors if required. That allows for clean teardown and avoids everything
|
@@ -74,6 +76,13 @@ The configuration file knows two configuration variables:
|
|
74
76
|
options hash you might otherwise pass to `Driver.create` as the second
|
75
77
|
parameter.
|
76
78
|
|
79
|
+
See the documentation on [configuration features](docs/CONFIGURATION.md) for
|
80
|
+
details.
|
81
|
+
|
82
|
+
## Development
|
83
|
+
|
84
|
+
- [driver development](docs/DRIVERS.md)
|
85
|
+
|
77
86
|
# Credits
|
78
87
|
This gem is inspired by [LapisLazuli](https://github.com/spriteCloud/lapis-lazuli),
|
79
88
|
but vastly less complex, and aims to stay so.
|
data/docs/CONFIGURATION.md
CHANGED
@@ -170,7 +170,9 @@ drivers:
|
|
170
170
|
The tests can be run on device/browser farms. Typically you only need to
|
171
171
|
configure drivers, much like for mobile testing. The following example
|
172
172
|
is for [TestingBot](https://testingbot.com). Note that each farm expects
|
173
|
-
different configuration keys for selecting browsers and for authentication
|
173
|
+
different configuration keys for selecting browsers and for authentication,
|
174
|
+
and that `unobtainium` just passes these values through to Selenium and/or
|
175
|
+
Appium.
|
174
176
|
|
175
177
|
```yaml
|
176
178
|
# config/config.yml
|
data/docs/DRIVERS.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Drivers
|
2
|
+
|
3
|
+
Drivers are used to access web pages and their content. Since unobtainium is
|
4
|
+
designed to be primarily a wrapper for Selenium and Appium, the built-in
|
5
|
+
drivers provide roughly the same API, but there is no strict requirement for
|
6
|
+
this.
|
7
|
+
|
8
|
+
Driver implementations are classes which are required to have a number of
|
9
|
+
class methods, described below. No requirements on instance methods are made.
|
10
|
+
|
11
|
+
# Required Class Methods
|
12
|
+
|
13
|
+
The class methods required by unobtainium are as follows:
|
14
|
+
|
15
|
+
- `matches?` accepting a String or Symbol label, as passed by the user to
|
16
|
+
`Driver#create`. Must return true if the driver implementation matches this
|
17
|
+
label, i.e. if this driver implementation is to be used when the user
|
18
|
+
specifies this particular label.
|
19
|
+
Note that multiple driver implementations can match the same label; the order
|
20
|
+
of preference is an implementation detail.
|
21
|
+
- *[optional]* `resolve_options` accepting the label and options passed to
|
22
|
+
`Driver#create` by the user (defaulting to an empty hash). The function must
|
23
|
+
return the label and options again, however should normalize the label (see
|
24
|
+
below) and supplement any options with default values, etc.
|
25
|
+
- `ensure_preconditions` accepting the label, and options.
|
26
|
+
The method should be used to require any necessary code, and raise errors if
|
27
|
+
any other preconditions are not met. See the section on dynamic loading as
|
28
|
+
well.
|
29
|
+
- Finally, `create` accepting the label and options should return an instance
|
30
|
+
of a driver class matching both parameters.
|
31
|
+
|
32
|
+
# Optional Instance Methods
|
33
|
+
|
34
|
+
- `destroy` gets invoked by `Runtime` at exit, if `World#driver` is used to
|
35
|
+
create the instance. It can be used to tear down the driver instance cleanly.
|
36
|
+
|
37
|
+
# Label Normalization & Configuration Resolution
|
38
|
+
|
39
|
+
The built-in drivers respond to many different labels, some of which are
|
40
|
+
aliases for a *normalized* label. For example, you can specify `:headless`,
|
41
|
+
which is an alias for `:phantomjs`.
|
42
|
+
|
43
|
+
Label normalization should return `:phantomjs` in the example above.
|
44
|
+
|
45
|
+
The main goal behind configuration resolution is to provide a way for driver
|
46
|
+
implementations to translate configuration keys into a form better suited to
|
47
|
+
the implementation.
|
48
|
+
|
49
|
+
For example, the Selenium driver symbolizes keys, because `selenium-webdriver`
|
50
|
+
requires symbol keys. On the other hand, the [configuration](./CONFIGURATION.md)
|
51
|
+
system produces String keys only.
|
52
|
+
|
53
|
+
But you can use this step also to expand shortcut options. The Appium implentation
|
54
|
+
allows you to more simply specify some mobile browsers, expanding this into
|
55
|
+
capabilities required by Appium itself.
|
56
|
+
|
57
|
+
# Instance Management
|
58
|
+
|
59
|
+
The `World#driver` function registers driver implementations with `Runtime` to
|
60
|
+
be destroyed at exit.
|
61
|
+
|
62
|
+
In order to allow multiple driver instances for e.g. multi-browser testing, but
|
63
|
+
simultaneously manage instances as described above, the normalized label and
|
64
|
+
resolved configuration (see above) are used to generate unique keys.
|
65
|
+
|
66
|
+
The basic principle is that `World#driver` will be called multiple times in a
|
67
|
+
test suite. If it is invoked twice with the same parameters, the same instance
|
68
|
+
should be returned. Parameterless invocations should always return the same
|
69
|
+
instance, as defined by the configuration. For the pattern inclined reader, this
|
70
|
+
is an implementation of the [flyweight pattern](https://en.wikipedia.org/wiki/Flyweight_pattern).
|
71
|
+
|
72
|
+
Therefore, `resolve_options` should return identical results for two invocations
|
73
|
+
with *semantically* identical input.
|
74
|
+
|
75
|
+
# Dynamic Loading
|
76
|
+
|
77
|
+
In order not to create hard dependencies in unobtainium on specific versions of
|
78
|
+
Selenium, Appium and PhantomJS, these dependencies are only required when
|
79
|
+
`ensure_preconditions` is being invoked. That lets users decide which versions
|
80
|
+
to require, and skip libraries they do not use.
|
81
|
+
|
82
|
+
Your driver implementation does not have to follow the same pattern, unless you
|
83
|
+
want to see it merged into unobtainium itself.
|
84
|
+
|
85
|
+
# Registering an Implementation
|
86
|
+
|
87
|
+
When you have written your class to conform to the above API, all that is left
|
88
|
+
to do is to register it with unobtainium:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
class MyDriver
|
92
|
+
# implementatin
|
93
|
+
end # class MyDriver
|
94
|
+
|
95
|
+
::Unobtainium::Driver.register_implementation(MyDriver, __FILE__)
|
96
|
+
```
|
97
|
+
|
98
|
+
The second path parameter should always be set to `__FILE__`. It is used to
|
99
|
+
ensure that if your library is included multiple times, the driver does not
|
100
|
+
get registered more than once. On the other hand, a different implementation
|
101
|
+
with the same class name would raise an error when `register_implementation`
|
102
|
+
is invoked.
|
@@ -27,6 +27,17 @@ module Unobtainium
|
|
27
27
|
class PathedHash
|
28
28
|
include RecursiveMerge
|
29
29
|
|
30
|
+
DEFAULT_PROC = proc do |hash, key|
|
31
|
+
case key
|
32
|
+
when String
|
33
|
+
sym = key.to_sym
|
34
|
+
hash[sym] if hash.key?(sym)
|
35
|
+
when Symbol
|
36
|
+
str = key.to_s
|
37
|
+
hash[str] if hash.key?(str)
|
38
|
+
end
|
39
|
+
end.freeze
|
40
|
+
|
30
41
|
##
|
31
42
|
# Initializer. Accepts `nil`, hashes or pathed hashes.
|
32
43
|
#
|
@@ -38,6 +49,8 @@ module Unobtainium
|
|
38
49
|
@data = init.dup
|
39
50
|
end
|
40
51
|
@separator = '.'
|
52
|
+
|
53
|
+
@data.default_proc = DEFAULT_PROC
|
41
54
|
end
|
42
55
|
|
43
56
|
# @return [String] the separator is the character or pattern splitting paths.
|
@@ -106,6 +119,7 @@ module Unobtainium
|
|
106
119
|
# For write methods, we need to create intermediary hashes.
|
107
120
|
leaf = recursive_fetch(components, @data,
|
108
121
|
create: WRITE_METHODS.include?(method))
|
122
|
+
leaf.default_proc = DEFAULT_PROC
|
109
123
|
|
110
124
|
# If we have a leaf, we want to send the requested method to that
|
111
125
|
# leaf.
|
data/lib/unobtainium/version.rb
CHANGED
data/lib/unobtainium/world.rb
CHANGED
@@ -36,7 +36,11 @@ module Unobtainium
|
|
36
36
|
# Return the global configuration, loaded from `World#config_file`
|
37
37
|
def config
|
38
38
|
return ::Unobtainium::Runtime.instance.store_with_if(:config) do
|
39
|
-
|
39
|
+
begin
|
40
|
+
::Unobtainium::Config.load_config(::Unobtainium::World.config_file)
|
41
|
+
rescue Errno::ENOENT
|
42
|
+
{}
|
43
|
+
end
|
40
44
|
end
|
41
45
|
end
|
42
46
|
|
data/spec/pathed_hash_spec.rb
CHANGED
@@ -54,6 +54,31 @@ describe ::Unobtainium::PathedHash do
|
|
54
54
|
expect(ph["bar.nope"]).to eql nil
|
55
55
|
end
|
56
56
|
|
57
|
+
it "can be used with indifferent access from string key" do
|
58
|
+
sample = {
|
59
|
+
"foo" => 42,
|
60
|
+
}
|
61
|
+
ph = ::Unobtainium::PathedHash.new(sample)
|
62
|
+
|
63
|
+
expect(ph["foo"]).to eql 42
|
64
|
+
expect(ph[:foo]).to eql 42
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can be used with indifferent access from symbol key" do
|
68
|
+
sample = {
|
69
|
+
foo: 42,
|
70
|
+
bar: {
|
71
|
+
baz: 'quux',
|
72
|
+
}
|
73
|
+
}
|
74
|
+
ph = ::Unobtainium::PathedHash.new(sample)
|
75
|
+
|
76
|
+
expect(ph["foo"]).to eql 42
|
77
|
+
expect(ph[:foo]).to eql 42
|
78
|
+
|
79
|
+
expect(ph['bar.baz']).to eql 'quux'
|
80
|
+
end
|
81
|
+
|
57
82
|
it "treats a single separator as the root" do
|
58
83
|
sample = { "foo" => 42 }
|
59
84
|
ph = ::Unobtainium::PathedHash.new(sample)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
# Only start CodeClimate from travis
|
2
|
+
if ENV['CODECLIMATE_REPO_TOKEN']
|
3
|
+
require 'codeclimate-test-reporter'
|
4
|
+
CodeClimate::TestReporter.start
|
5
|
+
end
|
6
|
+
|
7
|
+
# Always start SimpleCov
|
1
8
|
require 'simplecov'
|
2
9
|
SimpleCov.start do
|
3
10
|
add_filter 'unobtainium/drivers'
|
4
11
|
end
|
5
|
-
|
6
|
-
require "codeclimate-test-reporter"
|
7
|
-
CodeClimate::TestReporter.start
|
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.3.
|
4
|
+
version: 0.3.1
|
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-
|
11
|
+
date: 2016-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -185,6 +185,7 @@ files:
|
|
185
185
|
- Rakefile
|
186
186
|
- config/config.yml
|
187
187
|
- docs/CONFIGURATION.md
|
188
|
+
- docs/DRIVERS.md
|
188
189
|
- features/step_definitions/steps.rb
|
189
190
|
- features/support/env.rb
|
190
191
|
- features/world.feature
|