unobtainium 0.2.1 → 0.3.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: 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
+ ```