unobtainium 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Unobtainium Demonstration](http://img.youtube.com/vi/82pYWG5uTnM/0.jpg)](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
|