unobtainium 0.2.1 → 0.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
  SHA1:
3
- metadata.gz: 234ce137a967d3cbf7343bc5dce74f6a3a606339
4
- data.tar.gz: 4bd93c4402adf6a379f1037b70958941018e805f
3
+ metadata.gz: 9921b8f7735c0d00216208fe5ad193525671fd8f
4
+ data.tar.gz: 1557e2461fc29570eb97f975d83a4b792b838862
5
5
  SHA512:
6
- metadata.gz: 7899a9aefc8410150b42f930d8a770fff850883284026551b25edd9e30bdf152117b5510e88e192b2e21b097346e5cc8e773ee2bdbe945bf5a72a4641032e683
7
- data.tar.gz: f718847acf4c4600caa0f5a7b37dd76c0e5cf468b3d1993582aae3bdfbb2092d4fcc94ece7942c6e906029dbc0cf825a6dc491438981b68c140634be9a3c4846
6
+ metadata.gz: bd7643c1a36ea61aa917629a922cd3535b408f70a4206e18d77cebcd11327e0cb13931589eef0b7367c9c243928fd9c23ea82196397fce2bc756db9edbed1f85
7
+ data.tar.gz: f435d50ed4e50e697dbe1b7b1a3c0ddf5ccc50c2148d4d02cb5fea2ac30e7d738e59c47371655a4ccb449bb56c24a66760abebd7838fc22dd0cdecd973c00af6
data/.gitignore CHANGED
@@ -37,3 +37,7 @@ build/
37
37
 
38
38
  # Editor files
39
39
  .*.sw*
40
+
41
+ # Cucumber
42
+ C:\\nppdf32Log\\debuglog.txt
43
+ config/*-local.yml
data/.rubocop.yml CHANGED
@@ -58,3 +58,9 @@ Style/TrailingCommaInLiteral:
58
58
 
59
59
  Style/FirstParameterIndentation:
60
60
  Enabled: false
61
+
62
+ Style/TrailingUnderscoreVariable:
63
+ Enabled: false
64
+
65
+ Style/NumericLiterals:
66
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -2,18 +2,48 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  unobtainium (0.2.1)
5
+ sys-proctable (~> 1.0)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ appium_lib (8.0.2)
11
+ awesome_print (~> 1.6)
12
+ json (~> 1.8)
13
+ nokogiri (~> 1.6.6)
14
+ selenium-webdriver (~> 2.49)
15
+ tomlrb (~> 1.1)
9
16
  ast (2.2.0)
17
+ awesome_print (1.6.1)
18
+ builder (3.2.2)
19
+ childprocess (0.5.9)
20
+ ffi (~> 1.0, >= 1.0.11)
10
21
  codeclimate-test-reporter (0.5.0)
11
22
  simplecov (>= 0.7.1, < 1.0.0)
23
+ cucumber (2.3.3)
24
+ builder (>= 2.1.2)
25
+ cucumber-core (~> 1.4.0)
26
+ cucumber-wire (~> 0.0.1)
27
+ diff-lcs (>= 1.1.3)
28
+ gherkin (~> 3.2.0)
29
+ multi_json (>= 1.7.5, < 2.0)
30
+ multi_test (>= 0.1.2)
31
+ cucumber-core (1.4.0)
32
+ gherkin (~> 3.2.0)
33
+ cucumber-wire (0.0.1)
12
34
  diff-lcs (1.2.5)
13
35
  docile (1.1.5)
36
+ ffi (1.9.10)
37
+ gherkin (3.2.0)
14
38
  json (1.8.3)
39
+ mini_portile2 (2.0.0)
40
+ multi_json (1.11.2)
41
+ multi_test (0.1.2)
42
+ nokogiri (1.6.7.2)
43
+ mini_portile2 (~> 2.0.0.rc2)
15
44
  parser (2.3.0.7)
16
45
  ast (~> 2.2)
46
+ phantomjs (2.1.1.0)
17
47
  powerpack (0.1.1)
18
48
  rainbow (2.1.0)
19
49
  rake (11.1.2)
@@ -37,24 +67,38 @@ GEM
37
67
  ruby-progressbar (~> 1.7)
38
68
  unicode-display_width (~> 1.0, >= 1.0.1)
39
69
  ruby-progressbar (1.7.5)
70
+ rubyzip (1.2.0)
71
+ selenium-webdriver (2.53.0)
72
+ childprocess (~> 0.5)
73
+ rubyzip (~> 1.0)
74
+ websocket (~> 1.0)
40
75
  simplecov (0.11.2)
41
76
  docile (~> 1.1.0)
42
77
  json (~> 1.8)
43
78
  simplecov-html (~> 0.10.0)
44
79
  simplecov-html (0.10.0)
80
+ sys-proctable (1.0.0)
81
+ tomlrb (1.2.1)
45
82
  unicode-display_width (1.0.3)
83
+ websocket (1.2.3)
84
+ yard (0.8.7.6)
46
85
 
47
86
  PLATFORMS
48
87
  ruby
49
88
 
50
89
  DEPENDENCIES
90
+ appium_lib
51
91
  bundler (~> 1.11)
52
92
  codeclimate-test-reporter
93
+ cucumber
94
+ phantomjs
53
95
  rake (~> 11.1)
54
96
  rspec (~> 3.4)
55
97
  rubocop (~> 0.39)
98
+ selenium-webdriver
56
99
  simplecov (~> 0.11)
57
100
  unobtainium!
101
+ yard (~> 0.8)
58
102
 
59
103
  BUNDLED WITH
60
104
  1.11.2
data/README.md CHANGED
@@ -9,6 +9,8 @@ so that test code can more easily cover:
9
9
  - Mobile browsers
10
10
  - Mobile apps
11
11
 
12
+ The gem also wraps [PhantomJS](http://phantomjs.org/) for headless testing.
13
+
12
14
  Some additional useful functionality for the maintenance of test suites is
13
15
  also added.
14
16
 
@@ -26,26 +28,27 @@ Unobtainium's functionality is in standalone classes, but it's all combined in
26
28
  the `Unobtainium::World` module.
27
29
 
28
30
  - The `PathedHash` class extends `Hash` by allowing paths to nested values, e.g.:
29
- ```ruby
30
- h = PathedHash.new { "foo" => { "bar" => 42 }}
31
- h["foo.bar"] == 42 # true
32
- ```
33
- - The `Config` class is a `PathedHash`, but also reads JSON or YAML files to
34
- initialize itself with values, and allows config paths to be overridden by
35
- environment variables: `FOO_BAR` overrides the "foo.bar" path.
36
31
 
37
- You can use JSON as environment variable values.
32
+ ```ruby
33
+ h = PathedHash.new { "foo" => { "bar" => 42 }}
34
+ h["foo.bar"] == 42 # true
35
+ ```
36
+
37
+ - The `Config` class is a `PathedHash`, but also reads JSON or YAML files to
38
+ initialize itself with values. See the documentation on [configuration features](docs/CONFIGURATION.md)
39
+ for details
38
40
  - The `Runtime` class is a singleton and a `Hash`-like container (but simpler),
39
41
  that destroys all of its contents at the end of a script, calling custom
40
42
  destructors if required. That allows for clean teardown and avoids everything
41
- to have to implement the Singleton pattern itself.
43
+ having to implement the Singleton pattern itself.
42
44
  - The `Driver` class, of course, wraps either of Appium or Selenium drivers:
43
- ```ruby
44
- drv = Driver.create(:firefox) # uses Selenium
45
- drv = Driver.create(:android) # uses Appium
46
45
 
47
- drv.navigate.to "..." # delegates to Selenium or Appium
48
- ```
46
+ ```ruby
47
+ drv = Driver.create(:firefox) # uses Selenium
48
+ drv = Driver.create(:android) # uses Appium
49
+
50
+ drv.navigate.to "..." # delegates to Selenium or Appium
51
+ ```
49
52
 
50
53
  ## World
51
54
 
data/Rakefile CHANGED
@@ -1,16 +1,32 @@
1
1
  # Rubocop
2
2
  require 'rubocop/rake_task'
3
- RuboCop::RakeTask.new
3
+ RuboCop::RakeTask.new(:rubocop)
4
4
 
5
5
  # Rspec
6
6
  require 'rspec/core/rake_task'
7
- RSpec::Core::RakeTask.new(:spec)
7
+ RSpec::Core::RakeTask.new(:rspec)
8
+
9
+ # Cucumber
10
+ require 'cucumber'
11
+ require 'cucumber/rake/task'
12
+ Cucumber::Rake::Task.new(:cuke) do |t|
13
+ t.cucumber_opts = "--fail-fast --format=pretty --expand "\
14
+ "--order=random --backtrace"
15
+ end
16
+
17
+ # Documentation
18
+ require 'yard'
19
+ YARD::Rake::YardocTask.new do |t|
20
+ t.files = ['lib/**/*.rb']
21
+ t.options = ['-m', 'markdown']
22
+ t.stats_options = ['--list-undoc']
23
+ end
8
24
 
9
25
  # Combined test task
10
- desc "Test the code"
26
+ desc "Test all the things!"
11
27
  task :test do
12
28
  Rake::Task[:rubocop].invoke
13
- Rake::Task[:spec].invoke
29
+ Rake::Task[:rspec].invoke
14
30
  end
15
31
 
16
32
  # Default is the test task
data/config/config.yml ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ drivers:
3
+ firefox:
4
+ android:
5
+ browser: chrome
6
+ android_remote:
7
+ extends: remote
8
+ desired_capabilities:
9
+ platform: "ANDROID"
10
+ browserName: "galaxy_nexus"
11
+ remote:
12
+ url: "http://hub.testingbot.com:4444/wd/hub"
13
+ driver: firefox
14
+ at_end: quit
@@ -0,0 +1,250 @@
1
+ # Configuration
2
+
3
+ The `Config` class, and it's major user, the `World.config` method make using
4
+ complex configuration files in your project easier.
5
+
6
+ ## Basic Configuration Features
7
+
8
+ ### Pathed Access
9
+
10
+ `Config` is a `Hash` with pathed access. Take a nested structure such as this
11
+ as an example:
12
+
13
+ ```yaml
14
+ ---
15
+ foo:
16
+ bar: baz
17
+ ```
18
+
19
+ Instead of accessing `config['foo']['bar']`, you can instead use a path such
20
+ as `config['foo.bar']`. The major benefit is that if *any* of the path components
21
+ does not exist, nil is returned (and the behaviour is equivalent to other access
22
+ methods such as `:fetch`, etc.)
23
+
24
+ Similarly you can use this type of access for writing: `config['baz.quux'] = 42`
25
+ will create both the `baz` hash, and it's child the `quux` key.
26
+
27
+ ### Configuration Loading
28
+
29
+ If you're using `World.config`, configuration is automatically loaded at the
30
+ time it's first invoked from the location specified by `World.config_file`, which
31
+ defaults to `config/config.yml`.
32
+
33
+ This is equivalent to invoking `Config.load_config` with that location manually.
34
+
35
+ ### Local Configuration Overrides
36
+
37
+ For the example file of `config/config.yml`, if a file with the same path and
38
+ name, and the name postfix `-local` exists (i.e. `config/config-local.yml`), that
39
+ file will also be loaded. It's keys will be recursively added to the keys from the
40
+ main configuration file, overwriting only leaves, not entire hashes.
41
+
42
+ Example:
43
+
44
+ ```yaml
45
+ # config/config.yml
46
+ ---
47
+ foo:
48
+ bar: 42
49
+ baz: quux
50
+
51
+ # config/config-local.yml
52
+ ---
53
+ something: else
54
+ foo:
55
+ baz: override
56
+
57
+ # result
58
+ ---
59
+ something: else
60
+ foo:
61
+ bar: 42
62
+ baz: override
63
+ ```
64
+
65
+ ### Configuration Extension
66
+
67
+ An additional feature of the `Config` class is that you can extend individual
68
+ hashes with values from other hashes.
69
+
70
+ ```yaml
71
+ ---
72
+ root:
73
+ foo: bar
74
+ derived:
75
+ baz: quux
76
+ extends: root
77
+ ```
78
+
79
+ This results in:
80
+
81
+ ```yaml
82
+ ---
83
+ root:
84
+ foo: bar
85
+ derived:
86
+ baz: quux
87
+ foo: bar
88
+ base: root
89
+ ```
90
+
91
+ **Notes:**
92
+
93
+ - This feature means that `extends` and `base` are reserved configuration keys!
94
+ - Multiple levels of extension are supported. The `base` keyword will *always
95
+ name the root-most element*, not the immediate ancestor.
96
+ - Extending from multiple bases is not supported.
97
+ - Extending from nonexistent bases is supported; all that happens is that the
98
+ `base` key is set.
99
+
100
+ ### Environment Variable Override
101
+
102
+ Given a configuration path, any environment variable with the same name (change
103
+ path to upper case letters and replace `.` with `_`, e.g. `foo.bar` becomes
104
+ `FOO_BAR`) overrides the values in the configuration file.
105
+
106
+ If the environment variable is parseable as JSON, then that parsed JSON will
107
+ **replace** the original configuration path (i.e. it will not be merged).
108
+
109
+ ## Configuration Values Interpreted by World
110
+
111
+ - The `driver` path specifies which driver to use; the value shold be a label
112
+ recognized by the `Driver.create` method.
113
+ - The `drivers` (plural) path contains options for the different drivers. Each
114
+ driver configuration is nested under a key matching a `Driver.create` label,
115
+ e.g.
116
+ ```yaml
117
+ ---
118
+ drivers:
119
+ firefox:
120
+ some: option
121
+ ```
122
+ - The `at_end` path specifies what should be done with the driver when the
123
+ script ends. Possible values are `close` and `quit`, and the default is `quit`.
124
+
125
+ ## Driver Configurations
126
+
127
+ You can create quite complex driver configurations with the above features, for
128
+ very convenient test suite development.
129
+
130
+ ### Select Driver
131
+
132
+ Typically, you will configure all drivers in use by your test suite in the
133
+ `drivers` section of the configuration file. Then, use the `DRIVER` environment
134
+ variable to override/set which driver to use:
135
+
136
+ ```bash
137
+ $ DRIVER=chrome bundle exec my_tests
138
+ ```
139
+
140
+ ### Mobile
141
+
142
+ When running mobile test suites, [Appium](https://github.com/appium/appium)
143
+ requires that you identify the app and/or device you want to run tests against.
144
+
145
+ That typically means specifying parts of a configuration that is applicable to
146
+ any user of the test code, and specifying parts that are applicable only to an
147
+ individual.
148
+
149
+ Use the local configuration override to achieve this split:
150
+
151
+ ```yaml
152
+ # config/config.yml
153
+ ---
154
+ drivers:
155
+ android:
156
+ caps:
157
+ platformName: android
158
+ ...
159
+
160
+ # config/config-local.yml
161
+ ---
162
+ drivers:
163
+ android:
164
+ caps:
165
+ deviceName: deadbeef
166
+ ```
167
+
168
+ ### Browser/Device Farms
169
+
170
+ The tests can be run on device/browser farms. Typically you only need to
171
+ configure drivers, much like for mobile testing. The following example
172
+ is for [TestingBot](https://testingbot.com). Note that each farm expects
173
+ different configuration keys for selecting browsers and for authentication.
174
+
175
+ ```yaml
176
+ # config/config.yml
177
+ drivers:
178
+ remote:
179
+ url: "http://hub.testingbot.com:4444/wd/hub"
180
+ desired_capabilities:
181
+ platform: "WIN8"
182
+ browserName: "chrome"
183
+ version: "35"
184
+ ```
185
+
186
+ It's good practice to keep authentication data out of the github repository,
187
+ so the TestingBot API key and secret should live only in `config/config-local.yml`.
188
+
189
+ ```yaml
190
+ # config/config-local.yml
191
+ drivers:
192
+ remote:
193
+ desired_capabilities:
194
+ api_key: "1ceb00da"
195
+ api_secret: "cefaedfe"
196
+ ```
197
+
198
+ Then run:
199
+
200
+ ```bash
201
+ $ DRIVER=remote bundle exec cucumber
202
+ ```
203
+
204
+ ### Complex Configurations
205
+
206
+ With device/browser farms in particular, you typically do not want to use a
207
+ single `remote` configuration, but rather one for each remote browser or
208
+ device you want to use in testing. This is possible with the configuration
209
+ extension mechanism:
210
+
211
+ ```yaml
212
+ # config/config.yml
213
+ ---
214
+ drivers:
215
+ remote:
216
+ url: "http://hub.testingbot.com:4444/wd/hub"
217
+ remote_win8_chrome:
218
+ extends: remote
219
+ desired_capabilities:
220
+ platform: "WIN8"
221
+ browserName: "chrome"
222
+ version: "35"
223
+
224
+ # config/config-local.yml
225
+ ---
226
+ drivers:
227
+ remote:
228
+ desired_capabilities:
229
+ api_key: "1ceb00da"
230
+ api_secret: "cefaedfe"
231
+ ```
232
+
233
+ As noted in the section on the extension mechanism, the `extends` keyword
234
+ gets replaced with a `base` keyword that contains the *root-most* element.
235
+ In this case, `drivers.remote_win8_chrome.base` becomes `remote`, but even if
236
+ you had a `drivers.remote_win8_chrome38` section that overrides the desired
237
+ chrome version, `drivers.remote_win8_chrome38.base` would still become
238
+ `remote`.
239
+
240
+ The `World.config` method makes use of this, and replaces the driver label
241
+ you specified with the `driver` path or `DRIVER` environment variable with
242
+ this `base` value. The effect is that you can give your driver configuration
243
+ sections any name, as long as they're eventually extended by a section with
244
+ a key that `Driver.create` recognizes.
245
+
246
+ Thus, the following starts Chrome 35 on Windows 8 on TestingBot:
247
+
248
+ ```bash
249
+ $ DRIVER=remote_win8_chrome bundle exec my_test
250
+ ```