kraken-mobile 1.0.5 → 1.0.8
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/README.md +165 -59
- data/bin/kraken-mobile +9 -0
- data/bin/kraken_mobile_helpers.rb +16 -0
- data/bin/kraken_mobile_setup.rb +5 -1
- data/lib/kraken-mobile/device_process.rb +36 -0
- data/lib/kraken-mobile/helpers/kraken_faker.rb +2 -1
- data/lib/kraken-mobile/helpers/reporter.rb +3 -0
- data/lib/kraken-mobile/mobile/mobile_process.rb +23 -4
- data/lib/kraken-mobile/models/feature_file.rb +38 -11
- data/lib/kraken-mobile/models/web_device.rb +5 -2
- data/lib/kraken-mobile/monkeys/web/web_monkey.rb +63 -0
- data/lib/kraken-mobile/runners/calabash/android/steps/communication_steps.rb +1 -0
- data/lib/kraken-mobile/steps/general_steps.rb +1 -0
- data/lib/kraken-mobile/steps/mobile/kraken_steps.rb +1 -1
- data/lib/kraken-mobile/steps/web/kraken_steps.rb +20 -10
- data/lib/kraken-mobile/test_scenario.rb +46 -12
- data/lib/kraken-mobile/utils/k.rb +1 -0
- data/lib/kraken-mobile/utils/reporter.rb +49 -2
- data/lib/kraken-mobile/version.rb +1 -1
- data/lib/kraken-mobile/web/web_process.rb +5 -3
- data/lib/kraken_mobile.rb +23 -19
- data/reporter/feature_report.html.erb +5 -1
- data/reporter/index.html.erb +9 -3
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b00ad0b9cbfa1874a61ff9285fddf9616901c2e99db685b9fcfa05099bba3d6
|
4
|
+
data.tar.gz: 9370f8c54723f070282457dfca37a51bd4e4522fcc94ba14bb38f401b2db7fb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f9f6c735b8cfc759b6f9b8dec02bf833b4abe3fe8f450647af3303e4e9c7e3d9634e49b524b9931d6f6ecba31b00e64453b6b1d0b0a9a97ce3057433225d567
|
7
|
+
data.tar.gz: cf4c0fc443b23308a2c226e9523c39a178144cae805a5751ef0256a31840002f15521361e930ff02d4144e0e5345b0b6139b2dbf1added31ec1bf61eb25d8731
|
data/README.md
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
<p align="center">
|
2
2
|
<img src="./reporter/assets/images/kraken.png" alt="kraken logo" width="140" height="193">
|
3
|
+
<h1 align="center">Kraken</h1>
|
3
4
|
|
4
|
-
|
5
|
-
<h1 align="center">Kraken Mobile</h1>
|
6
|
-
|
7
|
-
Kraken is an open source automated android E2E testing tool that supports and validates scenarios that involve the inter-communication between two or more users. It works in a Black Box manner meaning that it is not required to have access to the source code of the application but instead it can be run with the APK (Android package file format). Kraken uses signaling for coordinating the communication between the devices using a file based protocol.
|
5
|
+
Kraken is an open source automated android and web E2E testing tool that supports and validates scenarios that involve the inter-communication between two or more users. It works in a Black Box manner meaning that it is not required to have access to the source code of the application but instead it can be run with the APK (Android package file format) and web page URL. Kraken uses signaling for coordinating the communication between the devices using a file based protocol.
|
8
6
|
|
9
7
|
**Kraken is partially supported by a Google Latin America Research Award (LARA) 2018**
|
10
8
|
|
@@ -13,26 +11,36 @@ Kraken is an open source automated android E2E testing tool that supports and va
|
|
13
11
|
|
14
12
|
# Publications
|
15
13
|
|
16
|
-
-
|
14
|
+
- *“Kraken-Mobile: Cross-Device Interaction-based Testing of Android Apps”*, [William Ravelo-Méndez](https://ravelinx22.github.io/), [Camilo Escobar-Velásquez](https://caev03.github.io/), and [Mario Linares-Vásquez](https://profesores.virtual.uniandes.edu.co/mlinaresv/en/inicio-en/), *Proceedings of the 35th IEEE International Conference on Software Maintenance and Evolution ([ICSME’19](https://icsme2019.github.io/))*, Tool Demo Track, Cleveland, OH, USA, September 30th - October 4th, 2019, to appear 4 pages (52% Acceptance Rate) [[pdf](https://thesoftwaredesignlab.github.io/KrakenMobile/assets/pdfs/icsme19.pdf)][[bibtex](https://thesoftwaredesignlab.github.io/KrakenMobile/assets/pdfs/icsme19.bib)]
|
17
15
|
|
18
16
|
# Technologies
|
19
17
|
|
20
|
-
Kraken uses [calabash-android](https://github.com/calabash/calabash-android) for running automated E2E tests in each device or emulator and [cucumber](https://github.com/cucumber/cucumber-ruby) for running your feature files written with Gherkin sintax.
|
21
|
-
|
18
|
+
Kraken uses [calabash-android](https://github.com/calabash/calabash-android) and [selenium-webdriver](https://github.com/SeleniumHQ/selenium) for running automated E2E tests in each device or emulator and [cucumber](https://github.com/cucumber/cucumber-ruby) for running your feature files written with Gherkin sintax.
|
22
19
|
|
23
|
-
# Installation
|
20
|
+
# 🔨 Installation
|
24
21
|
|
25
22
|
### Prerequisites
|
26
23
|
|
27
|
-
Kraken requires Ruby 2.20 or higher but we recommend using ~2.3 version. We use calabash-android
|
24
|
+
Kraken requires Ruby 2.20 or higher but we recommend using ~2.3 version. We use calabash-android for mobile testing, you can check their prerequisites at this [link](https://github.com/calabash/calabash-android/blob/master/documentation/installation.md). For web testing we use selenium-webdriver, you can check their prerequisites as well at this [link](https://www.selenium.dev/selenium/docs/api/rb/).
|
25
|
+
|
26
|
+
- Ruby at least version 2.3.1
|
27
|
+
- Java JDK
|
28
|
+
- Android SDK
|
29
|
+
- Chromedriver (Version ~83 recommended) or Geckodriver (Only if you use Kraken web)
|
30
|
+
|
31
|
+
You need to have configured ANDROID_HOME, ANDROID_HOME/platform_tools and JAVA_HOME in your environment variables.
|
28
32
|
|
29
|
-
|
33
|
+
Installing and managing Kraken Gem is done through the gem command. To install Kraken’s gem run the following command.
|
34
|
+
|
35
|
+
```
|
30
36
|
$ gem install kraken-mobile
|
31
37
|
```
|
32
38
|
|
39
|
+
NOTE: If you are having trouble running Kraken in your Windows operating system refer to the this [guide](https://github.com/TheSoftwareDesignLab/KrakenMobile/blob/master/docs/WINDOWS_GUIDE.md) specifying steps to solve this issues.
|
40
|
+
|
33
41
|
# Signaling
|
34
42
|
|
35
|
-
Signaling is a protocol used for the communication of two or more devices running in parallel. It is based in the idea that each emulator or real device has a communication channel where he can receive signals sent from other devices which contain information or actions that are supposed to be executed. This type of protocol is commonly used in automated mobile E2E testing tools that validate scenarios involving the inter-communication and collaboration of two or more applications.
|
43
|
+
Signaling is a protocol used for the communication of two or more devices running in parallel. It is based in the idea that each browser, emulator or real device has a communication channel where he can receive signals sent from other devices which contain information or actions that are supposed to be executed. This type of protocol is commonly used in automated mobile E2E testing tools that validate scenarios involving the inter-communication and collaboration of two or more applications.
|
36
44
|
|
37
45
|
# Writing your first test
|
38
46
|
|
@@ -47,19 +55,24 @@ features
|
|
47
55
|
| |_app_life_cycle_hooks.rb
|
48
56
|
| |_env.rb
|
49
57
|
|_step_definitions
|
50
|
-
| |
|
58
|
+
| |_mobile_steps.rb
|
59
|
+
|_web
|
60
|
+
| |_step_definitions
|
61
|
+
| | |_web_steps.rb
|
62
|
+
| |_support
|
63
|
+
| | |_app_life_cycle_hooks.rb
|
51
64
|
|_my_first.feature
|
52
65
|
```
|
53
66
|
|
54
67
|
### Write a test
|
55
68
|
|
56
|
-
The features goes in the features foler and should have the
|
69
|
+
The features goes in the features foler and should have the “.feature” extension. You can start out by looking at `features/my_first.feature`. You should also check calabash [predefined steps](https://github.com/calabash/calabash-android/blob/master/ruby-gem/lib/calabash-android/canned_steps.md).
|
57
70
|
|
58
71
|
### Syntax
|
59
72
|
|
60
73
|
In Kraken each feature is a test and each scenario within a feature is a test case that is run in a device. Each device is identified as an user and numbered from 1 to N. Ex: @user1, @user2, @user3. To check what is the number of a given device you should run `kraken-mobile devices`.
|
61
74
|
|
62
|
-
```
|
75
|
+
```
|
63
76
|
List of devices attached
|
64
77
|
user1 - emulator-5554 - Android_SDK_built_for_x86
|
65
78
|
user2 - emulator-5556 - Android_SDK_built_for_x86
|
@@ -67,9 +80,9 @@ user2 - emulator-5556 - Android_SDK_built_for_x86
|
|
67
80
|
|
68
81
|
After identifying what number each device has, you can write your test case giving each scenario the tag of a given device like so:
|
69
82
|
|
70
|
-
```
|
83
|
+
```
|
71
84
|
Feature: Example feature
|
72
|
-
|
85
|
+
|
73
86
|
@user1
|
74
87
|
Scenario: As a first user I say hi to a second user
|
75
88
|
Given I wait
|
@@ -88,9 +101,11 @@ Kraken offers two main steps to help synchronizing your devices.
|
|
88
101
|
### Signaling steps
|
89
102
|
|
90
103
|
To wait for a signal coming from another device for 10 seconds that is Kraken default timeout use the following step.
|
104
|
+
|
91
105
|
```
|
92
106
|
Then /^I wait for a signal containing "([^\"]*)"$/
|
93
107
|
```
|
108
|
+
|
94
109
|
To wait for a signal coming from another device for an specified number of seconds use the following step
|
95
110
|
|
96
111
|
```
|
@@ -105,34 +120,120 @@ Then /^I send a signal to user (\d+) containing "([^\"]*)"$/
|
|
105
120
|
|
106
121
|
### Signaling functions
|
107
122
|
|
108
|
-
|
123
|
+
Each device has an internal Kraken implementation of the signaling steps using the following functions.
|
109
124
|
|
110
|
-
```
|
111
|
-
|
125
|
+
```
|
126
|
+
read_signal(content, time_out)
|
112
127
|
```
|
113
128
|
|
114
|
-
Waits for a signal with the specified content
|
115
|
-
|
116
|
-
**Note: The channel parameter has to be the number of a device such as @user1, @user2, @userN**
|
129
|
+
Waits for a signal with the specified content. This functions waits for the specified number of seconds in the timeout parameter before throwing an exception if specified.
|
117
130
|
|
118
|
-
```
|
119
|
-
|
131
|
+
```
|
132
|
+
write_signal(signal)
|
120
133
|
```
|
121
134
|
|
122
|
-
Writes content to a
|
135
|
+
Writes signal content to a device inbox.
|
123
136
|
|
124
|
-
|
137
|
+
# 📱Kraken Mobile
|
125
138
|
|
126
|
-
|
139
|
+
## Running your tests
|
127
140
|
|
128
141
|
To run your test:
|
129
142
|
|
130
|
-
```
|
143
|
+
```
|
131
144
|
$ kraken-mobile run <apk>
|
132
145
|
```
|
133
146
|
|
134
147
|
Kraken with the help of Calabash-Android will install an instrumentation along with your app and will start your tests in all devices connected (Check Kraken Settings section in order to learn how to specify in what devices your tests should be run).
|
135
148
|
|
149
|
+
## 🦍 Mobile Monkey execution
|
150
|
+
|
151
|
+
Kraken offers the possibility of generating random GUI events by using Android ADB monkey as well as its own implementation based in the idea of sending and reading random signals.
|
152
|
+
|
153
|
+
### Android’s ADB Monkey
|
154
|
+
|
155
|
+
To execute ADB monkey Kraken offers the following command specifying the number of events to be executed:
|
156
|
+
|
157
|
+
```
|
158
|
+
Then I start a monkey with (\d+) events
|
159
|
+
```
|
160
|
+
|
161
|
+
### Kraken’s own monkey
|
162
|
+
|
163
|
+
Kraken extended the ADB monkey behavior by executing GUI events only in buttons and clickable views or inputs by offering the following command:
|
164
|
+
|
165
|
+
```
|
166
|
+
Then I start kraken monkey with (\d+) events
|
167
|
+
```
|
168
|
+
|
169
|
+
## XML snapshot
|
170
|
+
|
171
|
+
Kraken makes it possible to save the XML presented by the current view in a specific device, this is convenient to identify view ids, asserting the correct XML is presented after an action has being completed or running static analyzing tools.
|
172
|
+
|
173
|
+
### Saving the snapshot
|
174
|
+
|
175
|
+
To save the snapshot of the current view, Kraken offers the following step specifying where the file is going to be saved:
|
176
|
+
|
177
|
+
```
|
178
|
+
Then I save device snapshot in file with path "([^\"]*)"
|
179
|
+
```
|
180
|
+
|
181
|
+
# 🌎 Kraken Web
|
182
|
+
|
183
|
+
Kraken is extended to run also in web browsers and orchestrate the communication with other browsers running different websites or mobile applications that are being executed on physical devices or emulators. With the help of ChromeDriver/Geckodriver, selenium-webdriver and Cucumber we run test scenarios using Gherkin syntax as well as Kraken predefined signaling steps described before.
|
184
|
+
|
185
|
+
## Specifying a web user in a test
|
186
|
+
|
187
|
+
To specify that user is going to be run in a web browser instead of a mobile device you need to tag the scenario with @web. If a scenario is not tagged with @web Kraken will assume that is a mobile user and will attempt to run all steps in a mobile manner.
|
188
|
+
|
189
|
+
```bash
|
190
|
+
Feature: Example feature
|
191
|
+
|
192
|
+
@user1 @web
|
193
|
+
Scenario: As a first user I say hi to a second user
|
194
|
+
Given I navigate to page "https://www.gmail.com/"
|
195
|
+
Then I send a signal to user 2 containing "hi"
|
196
|
+
|
197
|
+
@user2 @web
|
198
|
+
Scenario: As a second user I wait for user 1 to say hi
|
199
|
+
Given I wait for a signal containing "hi"
|
200
|
+
Then I navigate to page "https://www.gmail.com/"
|
201
|
+
```
|
202
|
+
|
203
|
+
This scenario will open two web browsers and execute the signaling between them.
|
204
|
+
|
205
|
+
## Web steps
|
206
|
+
|
207
|
+
To see a list of all web steps available in Kraken, visit the following [link.](https://github.com/TheSoftwareDesignLab/KrakenMobile/blob/master/docs/WEB_STEPS.md)
|
208
|
+
|
209
|
+
## Executing web only scenarios
|
210
|
+
|
211
|
+
To run a test that only contains web users and as a result does not require an APK file, run the following command:
|
212
|
+
|
213
|
+
```
|
214
|
+
$ kraken-mobile run
|
215
|
+
```
|
216
|
+
|
217
|
+
Kraken with the help of selenium-webdriver will start all required browsers and run your test scenario.
|
218
|
+
|
219
|
+
## Specifying in what browser Kraken will run
|
220
|
+
|
221
|
+
Kraken uses ChromeDriver and Chrome as default web browser but provides support for Firefox and Geckodriver. To specify that your test is going to be run in Firefox, specify the environment variable BROWSER when running your tests as follows:
|
222
|
+
|
223
|
+
```bash
|
224
|
+
$ BROWSER=firefox kraken-mobile run
|
225
|
+
```
|
226
|
+
|
227
|
+
**Note: Make sure that you have Geckodriver and Firefox installed previously.**
|
228
|
+
|
229
|
+
## 🦍 Web Monkey execution
|
230
|
+
|
231
|
+
Kraken has implemented it's own monkey behavior by executing random GUI events in buttons, clickable views and inputs by offering the following command:
|
232
|
+
|
233
|
+
```
|
234
|
+
Then I start kraken monkey with (\d+) events
|
235
|
+
```
|
236
|
+
|
136
237
|
# Kraken Settings
|
137
238
|
|
138
239
|
Kraken uses kraken_mobile_settings.json to specify in what devices the tests should be run.
|
@@ -141,16 +242,20 @@ Kraken uses kraken_mobile_settings.json to specify in what devices the tests sho
|
|
141
242
|
|
142
243
|
The following command will show you the available connected devices or emulators and let you choose which ones you want to use.
|
143
244
|
|
144
|
-
```
|
245
|
+
```
|
145
246
|
$ kraken-mobile setup
|
146
247
|
```
|
147
248
|
|
148
249
|
### Run tests with settings file
|
149
250
|
|
150
|
-
```
|
251
|
+
```
|
151
252
|
$ kraken-mobile run <apk> --configuration=<kraken_mobile_settings_path>
|
152
253
|
```
|
153
254
|
|
255
|
+
# 🗯 Extended Kraken functionalities
|
256
|
+
|
257
|
+
In the following sections we provide specification for shared functionality between Kraken mobile and web as well as some examples of Kraken in action.
|
258
|
+
|
154
259
|
# Properties file
|
155
260
|
|
156
261
|
Kraken uses properties files to store sensitive data such as passwords or api keys that should be used in your test cases.
|
@@ -159,22 +264,22 @@ Kraken uses properties files to store sensitive data such as passwords or api ke
|
|
159
264
|
|
160
265
|
The properties files should be a manually created JSON file with the following structure.
|
161
266
|
|
162
|
-
```
|
267
|
+
```
|
163
268
|
{
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
269
|
+
"@user1": {
|
270
|
+
"PASSWORD": "test"
|
271
|
+
},
|
272
|
+
"@user2": {
|
273
|
+
"PASSWORD": "test2"
|
274
|
+
}
|
170
275
|
}
|
171
276
|
```
|
172
|
-
|
277
|
+
|
173
278
|
### Use properties file in your test
|
174
279
|
|
175
280
|
You can use the specified properties using the following sintax.
|
176
281
|
|
177
|
-
```
|
282
|
+
```
|
178
283
|
@user1
|
179
284
|
Scenario: As a user
|
180
285
|
Given I wait
|
@@ -191,17 +296,17 @@ kraken-mobile run <apk> --properties=<kraken_mobile_properties_path>
|
|
191
296
|
|
192
297
|
Kraken offers a Fake string generator thanks to the Ruby gem [Faker](https://github.com/faker-ruby/faker), the list of supported faker types are listed as follows:
|
193
298
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
299
|
+
- Name
|
300
|
+
- Number
|
301
|
+
- Email
|
302
|
+
- String
|
303
|
+
- String Date
|
199
304
|
|
200
305
|
### Use a faker in a test
|
201
306
|
|
202
|
-
Kraken keeps a record of every Fake string generated, thats why each string will have an id associated. To generate a Faker string you need to follow the structure
|
307
|
+
Kraken keeps a record of every Fake string generated, thats why each string will have an id associated. To generate a Faker string you need to follow the structure “$FAKERNAME_ID”.
|
203
308
|
|
204
|
-
```
|
309
|
+
```
|
205
310
|
@user1
|
206
311
|
Scenario: As a user
|
207
312
|
Given I wait
|
@@ -212,7 +317,7 @@ Scenario: As a user
|
|
212
317
|
|
213
318
|
As mentioned before, Kraken keeps record of every string generated with an id given to each string, this gives you the possibility of reusing this string later in your scenario. To reuse a string you can you need to append a $ character to the fake string as follows:
|
214
319
|
|
215
|
-
```
|
320
|
+
```
|
216
321
|
@user1
|
217
322
|
Scenario: As a user
|
218
323
|
Given I wait
|
@@ -223,15 +328,16 @@ Scenario: As a user
|
|
223
328
|
|
224
329
|
# Examples
|
225
330
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
331
|
+
|
332
|
+
| Application | Video | Feature File | Steps Definition | Properties file | Settings File | Report Link |
|
333
|
+
|:-------------|:-------------|:-------------|:------------------|:-------|:-------|:-------|
|
334
|
+
| Infinite Words | [video](https://www.youtube.com/watch?v=4lX7mO80w-4&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=3&t=0s)|[.feature](/examples/infinite-words/infinite_words.feature)|--- | --- | --- | [report](/examples/infinite-words/report/index.html) |
|
335
|
+
| QuizUp | [video](https://www.youtube.com/watch?v=2mhZVTK0r6k&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=4&t=1s) | [.feature](/examples/quizup/quizup.feature)|[stepsDef](../gh-pages/examples/quizup/step_definitions/kraken_steps.rb) | --- | --- | [report](/examples/quizup/report/index.html) |
|
336
|
+
| Spotify/Shazam | [video](https://www.youtube.com/watch?v=7AKsfY1KFX0&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=5&t=0s) | [.feature](/examples/shazam/shazam.feature)|[stepsDef](../gh-pages/examples/shazam/step_definitions/kraken_steps.rb) | [.json](/examples/shazam/kraken_properties.json) | [.json](/examples/shazam/kraken_mobile_settings.json) | [report](/examples/shazam/report/index.html) |
|
337
|
+
| Spunky | [video](https://www.youtube.com/watch?v=WOhRWkdFaVk&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=6&t=25s) | [.feature](/examples/spunky/spunky.feature)|[stepsDef](../gh-pages/examples/spunky/step_definitions/kraken_steps.rb) | --- | --- | [report](/examples/spunky/report/index.html) |
|
338
|
+
| Picap | [video](https://www.youtube.com/watch?v=RozQrmH_Z5k&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=7&t=3s) | [.feature](/examples/picap/picap.feature)|[stepsDef](../gh-pages/examples/picap/step_definitions/kraken_steps.rb) | [.json](/examples/picap/kraken_properties.json) | --- | [report](/examples/picap/report/index.html) |
|
339
|
+
| AskFM | [video](https://www.youtube.com/watch?v=d9Gbdx8kFX8&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=8&t=0s) | [.feature](/examples/askfm/askfm.feature)|[stepsDef](../gh-pages/examples/askfm/step_definitions/kraken_steps.rb) | --- | --- | [report](/examples/askfm/report/index.html) |
|
340
|
+
| Stick Men Fight | [video](https://www.youtube.com/watch?v=36OJKNj0nSo&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=9&t=4s) | [.feature](/examples/stick/stick.feature)|--- | --- | --- | [report](/examples/stick/report/index.html) |
|
341
|
+
| Tic Tac Toe | [video](https://www.youtube.com/watch?v=F9pDJDYsL_w&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=10&t=2s) | [.feature](/examples/tictactoe/tictactoe.feature)|[stepsDef](../gh-pages/examples/tictactoe/step_definitions/kraken_steps.rb) | --- | --- | [report](/examples/tictactoe/report/index.html) |
|
342
|
+
| Tumblr | [video](https://www.youtube.com/watch?v=eqFej2uJz4k&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=11&t=3s) | [.feature](/examples/tumblr/tumblr.feature)|[stepsDef](../gh-pages/examples/tumblr/step_definitions/kraken_steps.rb) | [.json](/examples/tumblr/kraken_properties.json) | --- | [report](/examples/tumblr/report/index.html) |
|
343
|
+
| F3 | [video](https://www.youtube.com/watch?v=vESh6Jyp-so&list=PLF5U8kfVgRcJ3RCHt7cWmwlqN93brbVW-&index=12&t=0s) | [.feature](/examples/f3/f3.feature)|[stepsDef](../gh-pages/examples/f3/step_definitions/kraken_steps.rb) | [.json](/examples/f3/kraken_properties.json) | --- | [report](/examples/f3/report/index.html) |
|
data/bin/kraken-mobile
CHANGED
@@ -62,13 +62,22 @@ else
|
|
62
62
|
when 'setup'
|
63
63
|
KrakenSetup.new.run
|
64
64
|
when 'gen'
|
65
|
+
ensure_java_installed
|
66
|
+
ensure_android_sdk_installed
|
67
|
+
|
65
68
|
scaffold_folder_structure
|
66
69
|
when 'resign'
|
70
|
+
ensure_java_installed
|
71
|
+
ensure_android_sdk_installed
|
72
|
+
|
67
73
|
require 'calabash-android/helpers'
|
68
74
|
puts 'Resigning APK with Calabash-Android'
|
69
75
|
ensure_apk_is_specified
|
70
76
|
resign_apk(user_entered_apk_path)
|
71
77
|
when 'run'
|
78
|
+
ensure_java_installed
|
79
|
+
ensure_android_sdk_installed
|
80
|
+
|
72
81
|
require File.join(File.dirname(__FILE__), 'kraken_mobile_calabash_android')
|
73
82
|
configuration = read_configuration
|
74
83
|
user_entered_properties_path = read_user_entered_properties_path
|
@@ -89,3 +89,19 @@ def ensure_properties_is_valid(properties)
|
|
89
89
|
puts 'The path of the properties file is not valid.'
|
90
90
|
exit 1
|
91
91
|
end
|
92
|
+
|
93
|
+
def ensure_android_sdk_installed
|
94
|
+
return unless ENV['ANDROID_HOME'].nil?
|
95
|
+
|
96
|
+
puts 'To use Kraken you need to have installed Android SDK first. '\
|
97
|
+
'Make sure you have the environment variable ANDROID_HOME configured'
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
def ensure_java_installed
|
102
|
+
return unless ENV['ANDROID_HOME'].nil?
|
103
|
+
|
104
|
+
puts 'To use Kraken you need to have installed Java first.'\
|
105
|
+
'Make sure you have the environment variable JAVA_HOME configured'
|
106
|
+
exit 1
|
107
|
+
end
|
data/bin/kraken_mobile_setup.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'io/console'
|
2
2
|
require 'tty-prompt'
|
3
|
+
$LOAD_PATH << File.expand_path('../lib', __dir__)
|
4
|
+
require 'kraken-mobile/utils/k.rb'
|
3
5
|
|
4
6
|
class KrakenSetup
|
5
7
|
WEB_IDENTIFIER = 'Web'.freeze
|
6
|
-
IS_WEB_AVAILABLE_FOR_SELECTION =
|
8
|
+
IS_WEB_AVAILABLE_FOR_SELECTION = true
|
7
9
|
|
8
10
|
attr_accessor :prompt
|
9
11
|
attr_accessor :devices_connected_id
|
@@ -85,6 +87,7 @@ class KrakenSetup
|
|
85
87
|
@settings[user_id] = {
|
86
88
|
id: device_id,
|
87
89
|
model: device.model,
|
90
|
+
type: K::ANDROID_DEVICE,
|
88
91
|
config: {
|
89
92
|
apk_path: apk
|
90
93
|
}
|
@@ -96,6 +99,7 @@ class KrakenSetup
|
|
96
99
|
@settings[user_id] = {
|
97
100
|
id: device.id,
|
98
101
|
model: device.model,
|
102
|
+
type: K::WEB_DEVICE,
|
99
103
|
config: {}
|
100
104
|
}
|
101
105
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
1
3
|
# Abstract class
|
2
4
|
class DeviceProcess
|
3
5
|
attr_accessor :id
|
@@ -43,12 +45,40 @@ class DeviceProcess
|
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
48
|
+
def unregister_process_from_directory
|
49
|
+
File.open(K::DIRECTORY_PATH, 'r') do |f|
|
50
|
+
File.open("#{K::DIRECTORY_PATH}.tmp", 'w') do |f2|
|
51
|
+
f.each_line do |line|
|
52
|
+
f2.write(line) unless line.start_with?(
|
53
|
+
"#{id}#{K::SEPARATOR}#{device}"
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
FileUtils.mv("#{K::DIRECTORY_PATH}.tmp", K::DIRECTORY_PATH)
|
59
|
+
end
|
60
|
+
|
46
61
|
def notify_ready_to_start
|
47
62
|
File.open(K::DIRECTORY_PATH, 'a') do |file|
|
48
63
|
file.puts("#{id}#{K::SEPARATOR}#{device}")
|
49
64
|
end
|
50
65
|
end
|
51
66
|
|
67
|
+
def running_on_windows?
|
68
|
+
RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/
|
69
|
+
end
|
70
|
+
|
71
|
+
def exporting_command_for_environment_variables(variables = {})
|
72
|
+
commands = variables.map do |key, value|
|
73
|
+
if running_on_windows?
|
74
|
+
"(SET \"#{key}=#{value}\")"
|
75
|
+
else
|
76
|
+
"#{key}=#{value};export #{key}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
commands.join(terminal_command_separator)
|
80
|
+
end
|
81
|
+
|
52
82
|
def self.directory
|
53
83
|
return [] unless File.exist?(K::DIRECTORY_PATH)
|
54
84
|
|
@@ -91,4 +121,10 @@ class DeviceProcess
|
|
91
121
|
|
92
122
|
devices_ready || []
|
93
123
|
end
|
124
|
+
|
125
|
+
def terminal_command_separator
|
126
|
+
return ' & ' if running_on_windows?
|
127
|
+
|
128
|
+
';'
|
129
|
+
end
|
94
130
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'faker'
|
2
|
-
require 'kraken-mobile/utils/k'
|
2
|
+
require 'kraken-mobile/utils/k.rb'
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
class KrakenFaker
|
@@ -56,6 +56,7 @@ class KrakenFaker
|
|
56
56
|
absolute_dictionary_path = File.expand_path(K::DICTIONARY_PATH)
|
57
57
|
file = open(absolute_dictionary_path)
|
58
58
|
content = file.read
|
59
|
+
file.close
|
59
60
|
JSON.parse(content)
|
60
61
|
end
|
61
62
|
|
@@ -26,6 +26,7 @@ module KrakenMobile
|
|
26
26
|
# Variables
|
27
27
|
report_file = open("#{KrakenMobile::Constants::REPORT_PATH}/#{@execution_id}/#{KrakenMobile::Constants::REPORT_DEVICES_FILE_NAME}.json")
|
28
28
|
content = report_file.read
|
29
|
+
report_file.close
|
29
30
|
@devices = JSON.parse(content)
|
30
31
|
devices_report = report_by_devices(@devices)
|
31
32
|
@features_report = fetures_from_report_by_devices devices_report
|
@@ -45,6 +46,7 @@ module KrakenMobile
|
|
45
46
|
@apk_path = device.config["apk_path"] ? device.config["apk_path"] : @options[:apk_path]
|
46
47
|
report_file = open("#{KrakenMobile::Constants::REPORT_PATH}/#{@execution_id}/#{device.id}/#{KrakenMobile::Constants::REPORT_FILE_NAME}.json")
|
47
48
|
content = report_file.read
|
49
|
+
report_file.close
|
48
50
|
@features = JSON.parse(content)
|
49
51
|
@total_scenarios = total_scenarios @features
|
50
52
|
@device = device
|
@@ -70,6 +72,7 @@ module KrakenMobile
|
|
70
72
|
next if !File.exists?("#{KrakenMobile::Constants::REPORT_PATH}/#{@execution_id}/#{device['id']}/#{KrakenMobile::Constants::REPORT_FILE_NAME}.json")
|
71
73
|
report_file = open("#{KrakenMobile::Constants::REPORT_PATH}/#{@execution_id}/#{device['id']}/#{KrakenMobile::Constants::REPORT_FILE_NAME}.json")
|
72
74
|
content = report_file.read
|
75
|
+
report_file.close
|
73
76
|
devices_report[device['user']] = JSON.parse(content)
|
74
77
|
devices_report[device['user']].each do |d| d["device_model"] = device["model"] if !d["device_model"] end
|
75
78
|
devices_report[device['user']].each do |d| d["device_id"] = device["id"] if !d["device_id"] end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'kraken-mobile/device_process.rb'
|
2
|
-
require 'kraken-mobile/utils/k'
|
2
|
+
require 'kraken-mobile/utils/k.rb'
|
3
3
|
|
4
4
|
class MobileProcess < DeviceProcess
|
5
5
|
#-------------------------------
|
@@ -11,6 +11,7 @@ class MobileProcess < DeviceProcess
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def after_execute
|
14
|
+
unregister_process_from_directory
|
14
15
|
device.delete_inbox
|
15
16
|
end
|
16
17
|
|
@@ -42,15 +43,32 @@ class MobileProcess < DeviceProcess
|
|
42
43
|
private
|
43
44
|
|
44
45
|
def execution_command
|
46
|
+
"|#{environment_variables_command}#{terminal_command_separator}"\
|
47
|
+
"#{running_process_command}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def running_process_command
|
45
51
|
feature_path = test_scenario.feature_file.file_path
|
46
52
|
raise 'ERROR: Invalid feature file path' if feature_path.nil?
|
47
53
|
|
48
54
|
process_apk_path = apk_path
|
49
55
|
raise 'ERROR: Invalid APK file path' if process_apk_path.nil?
|
50
56
|
|
51
|
-
"
|
52
|
-
|
53
|
-
|
57
|
+
"calabash-android run #{process_apk_path} \
|
58
|
+
#{feature_path} --tags @user#{id} \
|
59
|
+
--require features/support/env.rb \
|
60
|
+
--require features/support/app_installation_hooks.rb \
|
61
|
+
--require features/support/app_life_cycle_hooks.rb \
|
62
|
+
--require features/step_definitions/mobile_steps.rb \
|
63
|
+
--format pretty --format json -o \
|
64
|
+
#{K::REPORT_PATH}/#{@test_scenario.execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def environment_variables_command
|
68
|
+
variables = {
|
69
|
+
ADB_DEVICE_ARG: device.id
|
70
|
+
}
|
71
|
+
exporting_command_for_environment_variables(variables)
|
54
72
|
end
|
55
73
|
|
56
74
|
#-------------------------------
|
@@ -61,6 +79,7 @@ class MobileProcess < DeviceProcess
|
|
61
79
|
config_absolute_path = File.expand_path(ENV[K::CONFIG_PATH])
|
62
80
|
file = open(config_absolute_path)
|
63
81
|
content = file.read
|
82
|
+
file.close
|
64
83
|
JSON.parse(content)[@id.to_s] || {}
|
65
84
|
end
|
66
85
|
|
@@ -22,24 +22,49 @@ class FeatureFile
|
|
22
22
|
#-------------------------------
|
23
23
|
# Helpers
|
24
24
|
#-------------------------------
|
25
|
-
def
|
25
|
+
def user_tags
|
26
26
|
all_tags = @scenarios.map(&:tags).flatten.uniq
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
all_tags.select { |tag| tag.start_with?('@user') }
|
28
|
+
end
|
29
|
+
|
30
|
+
def system_tags
|
31
|
+
@scenarios.map do |scenario|
|
32
|
+
tags = scenario.tags
|
33
|
+
system_tag = tags.select do |tag|
|
34
|
+
tag.start_with?('@web') || tag.start_with?('@mobile')
|
35
|
+
end.first
|
36
|
+
system_tag || '@mobile'
|
31
37
|
end
|
32
|
-
|
38
|
+
end
|
39
|
+
|
40
|
+
def number_of_required_mobile_devices
|
41
|
+
system_tags.select { |tag| tag == '@mobile' }.count
|
33
42
|
end
|
34
43
|
|
35
44
|
def number_of_required_web_devices
|
36
|
-
|
37
|
-
all_tags.select { |tag| tag == '@web' }.count
|
45
|
+
system_tags.select { |tag| tag == '@web' }.count
|
38
46
|
end
|
39
47
|
|
40
48
|
def number_of_required_devices
|
41
|
-
|
42
|
-
|
49
|
+
user_tags.count
|
50
|
+
end
|
51
|
+
|
52
|
+
def required_devices
|
53
|
+
users = user_tags
|
54
|
+
systems = system_tags
|
55
|
+
|
56
|
+
users.map do |user|
|
57
|
+
{
|
58
|
+
user_id: user.delete_prefix('@user'),
|
59
|
+
system_type: systems.shift || '@mobile'
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def sorted_required_devices
|
65
|
+
required_devices.sort_by do |device|
|
66
|
+
device[:user_id].to_i
|
67
|
+
end
|
43
68
|
end
|
44
69
|
|
45
70
|
def tags_for_user_id(user_id)
|
@@ -95,7 +120,9 @@ class FeatureFile
|
|
95
120
|
|
96
121
|
def read_content
|
97
122
|
parser = Gherkin::Parser.new
|
98
|
-
|
123
|
+
file = File.open(file_path)
|
124
|
+
file_content = file.read
|
125
|
+
file.close
|
99
126
|
gherkin_document = parser.parse(file_content)
|
100
127
|
pickles = Gherkin::Pickles::Compiler.new.compile(gherkin_document)
|
101
128
|
pickles.each do |scenario|
|
@@ -5,7 +5,8 @@ class WebDevice < Device
|
|
5
5
|
# Signaling
|
6
6
|
#-------------------------------
|
7
7
|
def create_inbox
|
8
|
-
File.open(inbox_file_path, 'w')
|
8
|
+
file = File.open(inbox_file_path, 'w')
|
9
|
+
file.close
|
9
10
|
end
|
10
11
|
|
11
12
|
def delete_inbox
|
@@ -80,7 +81,9 @@ class WebDevice < Device
|
|
80
81
|
end
|
81
82
|
|
82
83
|
def inbox_last_signal
|
83
|
-
|
84
|
+
file = File.open(inbox_file_path)
|
85
|
+
lines = file.to_a
|
86
|
+
file.close
|
84
87
|
lines.last&.strip
|
85
88
|
end
|
86
89
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'selenium-webdriver'
|
2
|
+
require 'faker'
|
3
|
+
|
4
|
+
class WebMonkey
|
5
|
+
attr_accessor :driver
|
6
|
+
attr_accessor :wait
|
7
|
+
DEFAULT_WAIT_TIMEOUT = 0.5
|
8
|
+
|
9
|
+
def initialize(driver:)
|
10
|
+
self.driver = driver
|
11
|
+
self.wait = Selenium::WebDriver::Wait.new(timeout: DEFAULT_WAIT_TIMEOUT)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Helpers
|
15
|
+
def execute_kraken_monkey(number_of_events)
|
16
|
+
number_of_events.times do |_i|
|
17
|
+
execute_random_action
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute_random_action
|
22
|
+
arr = [
|
23
|
+
method(:random_click), method(:insert_random_text)
|
24
|
+
]
|
25
|
+
arr.sample.call
|
26
|
+
rescue StandardError => _e
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def random_click
|
31
|
+
element = @wait.until { driver.find_elements(:xpath, '//*').sample }
|
32
|
+
highlight_element(element)
|
33
|
+
element.click
|
34
|
+
remove_element_highlight(element)
|
35
|
+
end
|
36
|
+
|
37
|
+
def insert_random_text
|
38
|
+
element = @wait.until { driver.find_elements(:xpath, '//input').sample }
|
39
|
+
highlight_element(element)
|
40
|
+
element.click
|
41
|
+
text = [Faker::Lorem.word, Faker::Lorem.sentence].sample
|
42
|
+
element.send_keys(text)
|
43
|
+
remove_element_highlight(element)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def highlight_element(element)
|
49
|
+
@driver.execute_script(
|
50
|
+
"arguments[0].setAttribute('style', arguments[1]);",
|
51
|
+
element,
|
52
|
+
'color: red; border: 2px solid red'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove_element_highlight(element)
|
57
|
+
@driver.execute_script(
|
58
|
+
"arguments[0].setAttribute('style', arguments[1]);",
|
59
|
+
element,
|
60
|
+
''
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
@@ -9,6 +9,7 @@ ParameterType(
|
|
9
9
|
s.slice!(">")
|
10
10
|
file = open(ENV["PROPERTIES_PATH"])
|
11
11
|
content = file.read
|
12
|
+
file.close
|
12
13
|
properties = JSON.parse(content)
|
13
14
|
raise "Property <#{s}> not found for #{channel}" if !properties[channel] || !properties[channel][s]
|
14
15
|
return properties[channel][s]
|
@@ -1,30 +1,35 @@
|
|
1
|
+
require 'kraken-mobile/monkeys/web/web_monkey'
|
2
|
+
require 'kraken-mobile/steps/general_steps'
|
3
|
+
require 'kraken-mobile/utils/k.rb'
|
1
4
|
require 'selenium-webdriver'
|
2
5
|
require 'uri'
|
3
6
|
|
4
|
-
|
7
|
+
Before do
|
8
|
+
@driver = Selenium::WebDriver.for((ENV['BROWSER'] || 'chrome').to_sym)
|
9
|
+
end
|
5
10
|
|
6
11
|
Given(/^I navigate to page "([^\"]*)"$/) do |web_url|
|
7
12
|
raise 'ERROR: Invalid URL' if web_url.nil?
|
8
13
|
raise 'ERROR: Invalid URL' unless web_url =~ URI::DEFAULT_PARSER.make_regexp
|
9
14
|
|
10
|
-
driver.navigate.to web_url
|
15
|
+
@driver.navigate.to web_url
|
11
16
|
sleep 2
|
12
17
|
end
|
13
18
|
|
14
19
|
Then(/^I enter "([^\"]*)" into input field having id "([^\"]*)"$/) do |text, id|
|
15
|
-
driver.find_element(:id, id).send_keys(text)
|
20
|
+
@driver.find_element(:id, id).send_keys(text)
|
16
21
|
sleep 2
|
17
22
|
end
|
18
23
|
|
19
24
|
Then(
|
20
25
|
/^I enter "([^\"]*)" into input field having css selector "([^\"]*)"$/
|
21
26
|
) do |text, selector|
|
22
|
-
driver.find_element(:css, selector).send_keys(text)
|
27
|
+
@driver.find_element(:css, selector).send_keys(text)
|
23
28
|
sleep 2
|
24
29
|
end
|
25
30
|
|
26
31
|
Then(/^I click on element having id "(.*?)"$/) do |id|
|
27
|
-
driver.find_element(:id, id).click
|
32
|
+
@driver.find_element(:id, id).click
|
28
33
|
sleep 2
|
29
34
|
end
|
30
35
|
|
@@ -35,7 +40,7 @@ Then(/^I wait for (\d+) seconds$/) do |seconds|
|
|
35
40
|
end
|
36
41
|
|
37
42
|
Then(/^I should see text "(.*?)"$/) do |text|
|
38
|
-
driver.page_source.include?(text)
|
43
|
+
@driver.page_source.include?(text)
|
39
44
|
end
|
40
45
|
|
41
46
|
# Kraken Steps
|
@@ -73,9 +78,14 @@ Then(
|
|
73
78
|
device.read_signal(signal, seconds)
|
74
79
|
end
|
75
80
|
|
76
|
-
|
81
|
+
Then(/^I start a monkey with (\d+) events$/) do |number_of_events|
|
82
|
+
monkey = WebMonkey.new(driver: @driver)
|
83
|
+
monkey.execute_kraken_monkey(number_of_events)
|
84
|
+
end
|
77
85
|
|
78
|
-
|
79
|
-
|
80
|
-
|
86
|
+
# Hooks
|
87
|
+
AfterStep do |_scenario|
|
88
|
+
path = "#{ENV[K::SCREENSHOT_PATH]}/#{SecureRandom.hex(12)}.png"
|
89
|
+
@driver.save_screenshot(path)
|
90
|
+
embed(path, 'image/png', File.basename(path))
|
81
91
|
end
|
@@ -5,7 +5,7 @@ require 'kraken-mobile/models/web_device'
|
|
5
5
|
require 'kraken-mobile/web/web_process'
|
6
6
|
require 'kraken-mobile/utils/reporter'
|
7
7
|
require 'kraken-mobile/mobile/adb'
|
8
|
-
require 'kraken-mobile/utils/k'
|
8
|
+
require 'kraken-mobile/utils/k.rb'
|
9
9
|
require 'parallel'
|
10
10
|
|
11
11
|
class TestScenario
|
@@ -27,6 +27,9 @@ class TestScenario
|
|
27
27
|
@kraken_app = kraken_app
|
28
28
|
@execution_id = Digest::SHA256.hexdigest(Time.now.to_f.to_s)
|
29
29
|
@reporter = Reporter.new(test_scenario: self)
|
30
|
+
|
31
|
+
ensure_apk_specified_if_necessary
|
32
|
+
setup_scenario_environment_variables
|
30
33
|
end
|
31
34
|
|
32
35
|
#-------------------------------
|
@@ -70,11 +73,12 @@ class TestScenario
|
|
70
73
|
Parallel.map_with_index(
|
71
74
|
@devices, in_threads: @devices.count
|
72
75
|
) do |device, index|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
unless device.nil?
|
77
|
+
user_id = index + 1
|
78
|
+
start_process_for_user_id_in_device(
|
79
|
+
user_id, device
|
80
|
+
)
|
81
|
+
end
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
@@ -135,7 +139,11 @@ class TestScenario
|
|
135
139
|
def sample_devices
|
136
140
|
return predefined_devices if requires_predefined_devices?
|
137
141
|
|
138
|
-
|
142
|
+
mobile = sample_mobile_devices
|
143
|
+
web = sample_web_devices
|
144
|
+
@feature_file.sorted_required_devices.map do |device|
|
145
|
+
device[:system_type] == '@web' ? web.shift : mobile.shift
|
146
|
+
end
|
139
147
|
end
|
140
148
|
|
141
149
|
def sample_mobile_devices
|
@@ -181,13 +189,39 @@ class TestScenario
|
|
181
189
|
config_absolute_path = File.expand_path(ENV[K::CONFIG_PATH])
|
182
190
|
file = open(config_absolute_path)
|
183
191
|
content = file.read
|
192
|
+
file.close
|
184
193
|
devices_json = JSON.parse(content).values
|
185
|
-
|
186
194
|
devices_json.map do |device_json|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
195
|
+
if device_json['type'] == K::ANDROID_DEVICE
|
196
|
+
AndroidDevice.new(
|
197
|
+
id: device_json['id'], model: device_json['model']
|
198
|
+
)
|
199
|
+
elsif device_json['type'] == K::WEB_DEVICE
|
200
|
+
WebDevice.new(
|
201
|
+
id: device_json['id'], model: device_json['model']
|
202
|
+
)
|
203
|
+
else
|
204
|
+
raise 'ERROR: Platform not supported'
|
205
|
+
end
|
191
206
|
end
|
192
207
|
end
|
208
|
+
|
209
|
+
def apk_required?
|
210
|
+
sample_mobile_devices.any? && ENV[K::CONFIG_PATH].nil?
|
211
|
+
end
|
212
|
+
|
213
|
+
def ensure_apk_specified_if_necessary
|
214
|
+
return unless apk_required?
|
215
|
+
return unless @kraken_app&.apk_path.nil?
|
216
|
+
|
217
|
+
raise 'ERROR: Invalid APK file path'
|
218
|
+
end
|
219
|
+
|
220
|
+
def setup_scenario_environment_variables
|
221
|
+
return if @kraken_app.nil? || @reporter.nil?
|
222
|
+
|
223
|
+
@kraken_app.save_value_in_environment_variable_with_name(
|
224
|
+
name: K::SCREENSHOT_PATH, value: @reporter.screenshot_path
|
225
|
+
)
|
226
|
+
end
|
193
227
|
end
|
@@ -18,6 +18,7 @@ module K
|
|
18
18
|
REPORT_PATH = './reports' unless defined? REPORT_PATH
|
19
19
|
FILE_REPORT_NAME = 'report.json' unless defined? FILE_REPORT_NAME
|
20
20
|
D3_DATA_FILE_NAME = 'data.json' unless defined? D3_DATA_FILE_NAME
|
21
|
+
SCREENSHOT_PATH = 'SCREENSHOT_PATH' unless defined? SCREENSHOT_PATH
|
21
22
|
|
22
23
|
unless defined? DEVICES_REPORT_FILE_NAME
|
23
24
|
DEVICES_REPORT_FILE_NAME = 'devices.json'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'kraken-mobile/utils/k'
|
1
|
+
require 'kraken-mobile/utils/k.rb'
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
class Reporter
|
@@ -48,6 +48,7 @@ class Reporter
|
|
48
48
|
"#{K::REPORT_PATH}/#{test_execution_id}/#{K::DEVICES_REPORT_FILE_NAME}"
|
49
49
|
)
|
50
50
|
content = report_file.read
|
51
|
+
report_file.close
|
51
52
|
devices_report = report_by_devices
|
52
53
|
@features_report = fetures_from_report_by_devices(devices_report)
|
53
54
|
data_hash = feature_by_nodes_and_links @features_report
|
@@ -71,6 +72,16 @@ class Reporter
|
|
71
72
|
end
|
72
73
|
|
73
74
|
def generate_device_report(device, id)
|
75
|
+
if device.is_a? AndroidDevice
|
76
|
+
generate_mobile_report(device, id)
|
77
|
+
elsif device.is_a? WebDevice
|
78
|
+
generate_web_report(device, id)
|
79
|
+
else
|
80
|
+
raise 'ERROR: Platform not supported'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_mobile_report(device, id)
|
74
85
|
process = MobileProcess.new(
|
75
86
|
id: id,
|
76
87
|
device: device,
|
@@ -79,6 +90,36 @@ class Reporter
|
|
79
90
|
@apk_path = process.apk_path
|
80
91
|
report_file = open("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}")
|
81
92
|
content = report_file.read
|
93
|
+
report_file.close
|
94
|
+
@features = JSON.parse(content)
|
95
|
+
@total_scenarios = total_scenarios @features
|
96
|
+
@device = device
|
97
|
+
@total_failed_scenarios_percentage = total_failed_scenarios_percentage @features
|
98
|
+
@total_passed_scenarios_percentage = total_passed_scenarios_percentage @features
|
99
|
+
@total_passed_features_percentage = total_passed_features_percentage @features
|
100
|
+
@total_failed_features_percentage = total_failed_features_percentage @features
|
101
|
+
erb_file = File.join(File.expand_path('../../../../reporter', __FILE__), "feature_report.html.erb")
|
102
|
+
html_file = File.join(File.expand_path("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/"), File.basename(erb_file, '.erb')) #=>"page.html"
|
103
|
+
# Variables
|
104
|
+
template = File.read(erb_file)
|
105
|
+
result = ERB.new(template).result(binding)
|
106
|
+
# write result to file
|
107
|
+
File.open(html_file, 'w+') do |f|
|
108
|
+
f.write result
|
109
|
+
end
|
110
|
+
generate_features_report @features, device
|
111
|
+
end
|
112
|
+
|
113
|
+
def generate_web_report(device, id)
|
114
|
+
process = WebProcess.new(
|
115
|
+
id: id,
|
116
|
+
device: device,
|
117
|
+
test_scenario: @test_scenario
|
118
|
+
)
|
119
|
+
@apk_path = nil
|
120
|
+
report_file = open("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}")
|
121
|
+
content = report_file.read
|
122
|
+
report_file.close
|
82
123
|
@features = JSON.parse(content)
|
83
124
|
@total_scenarios = total_scenarios @features
|
84
125
|
@device = device
|
@@ -110,6 +151,7 @@ class Reporter
|
|
110
151
|
def create_report_execution_report_folder
|
111
152
|
Dir.mkdir(K::REPORT_PATH) unless File.exist?(K::REPORT_PATH)
|
112
153
|
Dir.mkdir("#{K::REPORT_PATH}/#{test_execution_id}")
|
154
|
+
Dir.mkdir(screenshot_path)
|
113
155
|
FileUtils.cp_r(
|
114
156
|
File.expand_path(K::REPORT_ASSETS_PATH, __FILE__),
|
115
157
|
"#{K::REPORT_PATH}/#{test_execution_id}/"
|
@@ -134,6 +176,10 @@ class Reporter
|
|
134
176
|
end
|
135
177
|
end
|
136
178
|
|
179
|
+
def screenshot_path
|
180
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/screenshots/"
|
181
|
+
end
|
182
|
+
|
137
183
|
private
|
138
184
|
|
139
185
|
def generate_features_report features, device
|
@@ -170,6 +216,7 @@ class Reporter
|
|
170
216
|
"#{K::REPORT_PATH}/#{test_execution_id}/#{device[:id]}/#{K::FILE_REPORT_NAME}"
|
171
217
|
)
|
172
218
|
content = report_file.read
|
219
|
+
report_file.close
|
173
220
|
devices_report[device[:user]] = JSON.parse(content)
|
174
221
|
devices_report[device[:user]].each do |d| d['device_model'] = device[:model] if !d['device_model'] end
|
175
222
|
devices_report[device[:user]].each do |d| d['device_id'] = device[:id] if !d['device_id'] end
|
@@ -316,7 +363,7 @@ class Reporter
|
|
316
363
|
def devices
|
317
364
|
raise 'ERROR: Invalid test scenario' if @test_scenario.nil?
|
318
365
|
|
319
|
-
@test_scenario.devices
|
366
|
+
@test_scenario.devices.compact
|
320
367
|
end
|
321
368
|
|
322
369
|
def devices_json
|
@@ -10,6 +10,7 @@ class WebProcess < DeviceProcess
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def after_execute
|
13
|
+
unregister_process_from_directory
|
13
14
|
device.delete_inbox
|
14
15
|
end
|
15
16
|
|
@@ -31,9 +32,10 @@ class WebProcess < DeviceProcess
|
|
31
32
|
feature_path = test_scenario.feature_file.file_path
|
32
33
|
raise 'ERROR: Invalid feature file path' if feature_path.nil?
|
33
34
|
|
34
|
-
#
|
35
|
-
"|cucumber --tags @user#{id}\
|
35
|
+
"|cucumber #{feature_path} --tags @user#{id} \
|
36
36
|
--require features/web/step_definitions/web_steps.rb \
|
37
|
-
--require features/web/support/app_life_cycle_hooks.rb
|
37
|
+
--require features/web/support/app_life_cycle_hooks.rb \
|
38
|
+
--format pretty --format json -o \
|
39
|
+
#{K::REPORT_PATH}/#{@test_scenario.execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}"
|
38
40
|
end
|
39
41
|
end
|
data/lib/kraken_mobile.rb
CHANGED
@@ -27,17 +27,37 @@ class KrakenApp
|
|
27
27
|
build_scenarios_queue
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
#-------------------------------
|
31
|
+
# Observers
|
32
|
+
#-------------------------------
|
33
|
+
def on_test_scenario_finished
|
31
34
|
execute_next_scenario
|
32
35
|
end
|
33
36
|
|
34
37
|
#-------------------------------
|
35
|
-
#
|
38
|
+
# Helpers
|
36
39
|
#-------------------------------
|
37
|
-
|
40
|
+
|
41
|
+
def start
|
38
42
|
execute_next_scenario
|
39
43
|
end
|
40
44
|
|
45
|
+
def save_path_in_environment_variable_with_name(name:, path:)
|
46
|
+
return if path.nil?
|
47
|
+
|
48
|
+
absolute_path = File.expand_path(path)
|
49
|
+
save_value_in_environment_variable_with_name(
|
50
|
+
name: name,
|
51
|
+
value: absolute_path
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def save_value_in_environment_variable_with_name(name:, value:)
|
56
|
+
return if name.nil? || value.nil?
|
57
|
+
|
58
|
+
ENV[name] = value
|
59
|
+
end
|
60
|
+
|
41
61
|
private
|
42
62
|
|
43
63
|
def build_scenarios_queue
|
@@ -58,20 +78,4 @@ class KrakenApp
|
|
58
78
|
scenario.run
|
59
79
|
scenario
|
60
80
|
end
|
61
|
-
|
62
|
-
def save_path_in_environment_variable_with_name(name:, path:)
|
63
|
-
return if path.nil?
|
64
|
-
|
65
|
-
absolute_path = File.expand_path(path)
|
66
|
-
save_value_in_environmant_variable_with_name(
|
67
|
-
name: name,
|
68
|
-
value: absolute_path
|
69
|
-
)
|
70
|
-
end
|
71
|
-
|
72
|
-
def save_value_in_environmant_variable_with_name(name:, value:)
|
73
|
-
return if name.nil? || value.nil?
|
74
|
-
|
75
|
-
ENV[name] = value
|
76
|
-
end
|
77
81
|
end
|
@@ -175,7 +175,11 @@
|
|
175
175
|
<%= device.model %> - <%= device.id %>
|
176
176
|
</td>
|
177
177
|
<td>
|
178
|
-
|
178
|
+
<% if device.type == K::ANDROID_DEVICE %>
|
179
|
+
<i class="fa fa-android fa-lg"><span>android</span></i>
|
180
|
+
<% else %>
|
181
|
+
<i class="fa fa-globe fa-lg"><span>web</span></i>
|
182
|
+
<% end %>
|
179
183
|
</td>
|
180
184
|
<td>
|
181
185
|
<%= @apk_path %>
|
data/reporter/index.html.erb
CHANGED
@@ -599,12 +599,18 @@
|
|
599
599
|
<div class="ui-card col-md-3">
|
600
600
|
<div class="container device-container">
|
601
601
|
<div class="row device-os-icon">
|
602
|
-
|
602
|
+
<% if device[:type] == K::ANDROID_DEVICE %>
|
603
|
+
<i class="fa fa-android fa-lg"></i>
|
604
|
+
<% else %>
|
605
|
+
<i class="fa fa-globe fa-lg"></i>
|
606
|
+
<% end %>
|
603
607
|
</div>
|
604
608
|
<div class="row device-title"><%= device[:model] %></div>
|
605
609
|
<div class="row device-info">ID - <%= device[:id] %></div>
|
606
|
-
|
607
|
-
|
610
|
+
<% if device[:type] == K::ANDROID_DEVICE %>
|
611
|
+
<div class="row device-info">SDK Version - <%= device[:sdk] %></div>
|
612
|
+
<div class="row device-info">Screen Size - <%= "#{device[:screen_height]}x#{device[:screen_width]}" %></div>
|
613
|
+
<% end %>
|
608
614
|
<div class="row device-info result-btn">
|
609
615
|
<a href='<%= "./#{device[:id]}/feature_report.html" %>'>See Results</a>
|
610
616
|
</div>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kraken-mobile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Ravelo M
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cucumber
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 3.
|
103
|
+
version: 3.14.0
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 3.
|
110
|
+
version: 3.14.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: faker
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- lib/kraken-mobile/models/web_device.rb
|
167
167
|
- lib/kraken-mobile/monkeys/mobile/android_monkey.rb
|
168
168
|
- lib/kraken-mobile/monkeys/mobile/kraken_android_monkey.rb
|
169
|
+
- lib/kraken-mobile/monkeys/web/web_monkey.rb
|
169
170
|
- lib/kraken-mobile/protocols/file_protocol.rb
|
170
171
|
- lib/kraken-mobile/runners/calabash/android/android_runner.rb
|
171
172
|
- lib/kraken-mobile/runners/calabash/android/apk_signer.rb
|
@@ -227,7 +228,7 @@ homepage: https://github.com/TheSoftwareDesignLab/KrakenMobile
|
|
227
228
|
licenses:
|
228
229
|
- MIT
|
229
230
|
metadata: {}
|
230
|
-
post_install_message:
|
231
|
+
post_install_message:
|
231
232
|
rdoc_options: []
|
232
233
|
require_paths:
|
233
234
|
- lib
|
@@ -242,8 +243,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
243
|
- !ruby/object:Gem::Version
|
243
244
|
version: '0'
|
244
245
|
requirements: []
|
245
|
-
rubygems_version: 3.0.
|
246
|
-
signing_key:
|
246
|
+
rubygems_version: 3.0.8
|
247
|
+
signing_key:
|
247
248
|
specification_version: 4
|
248
249
|
summary: Automated E2E mobile testing involving intercommunication scenarios.
|
249
250
|
test_files: []
|