kraken-mobile 1.0.2 → 1.0.9

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.
Files changed (54) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +251 -75
  3. data/bin/kraken-mobile +64 -67
  4. data/bin/kraken_mobile_calabash_android.rb +39 -0
  5. data/bin/kraken_mobile_helpers.rb +107 -0
  6. data/bin/kraken_mobile_setup.rb +138 -0
  7. data/calabash-android-features-skeleton/step_definitions/mobile_steps.rb +1 -0
  8. data/calabash-android-features-skeleton/support/app_installation_hooks.rb +2 -0
  9. data/calabash-android-features-skeleton/support/app_life_cycle_hooks.rb +3 -4
  10. data/calabash-android-features-skeleton/support/env.rb +1 -1
  11. data/calabash-android-features-skeleton/web/step_definitions/web_steps.rb +3 -0
  12. data/calabash-android-features-skeleton/web/support/app_life_cycle_hooks.rb +15 -0
  13. data/lib/kraken-mobile/device_process.rb +130 -0
  14. data/lib/kraken-mobile/helpers/devices_helper/adb_helper.rb +100 -105
  15. data/lib/kraken-mobile/helpers/kraken_faker.rb +108 -0
  16. data/lib/kraken-mobile/helpers/reporter.rb +3 -0
  17. data/lib/kraken-mobile/hooks/mobile_kraken_hooks.rb +15 -0
  18. data/lib/kraken-mobile/hooks/mobile_operations.rb +36 -0
  19. data/lib/kraken-mobile/hooks/web_operations.rb +33 -0
  20. data/lib/kraken-mobile/mobile/adb.rb +66 -0
  21. data/lib/kraken-mobile/mobile/android_commands.rb +43 -0
  22. data/lib/kraken-mobile/mobile/mobile_process.rb +101 -0
  23. data/lib/kraken-mobile/models/android_device.rb +121 -0
  24. data/lib/kraken-mobile/models/device.rb +113 -31
  25. data/lib/kraken-mobile/models/feature_file.rb +135 -0
  26. data/lib/kraken-mobile/models/feature_scenario.rb +24 -0
  27. data/lib/kraken-mobile/models/web_device.rb +89 -0
  28. data/lib/kraken-mobile/monkeys/mobile/android_monkey.rb +30 -0
  29. data/lib/kraken-mobile/monkeys/mobile/kraken_android_monkey.rb +54 -0
  30. data/lib/kraken-mobile/monkeys/web/web_monkey.rb +63 -0
  31. data/lib/kraken-mobile/runners/calabash/android/monkey_helper.rb +2 -2
  32. data/lib/kraken-mobile/runners/calabash/android/steps/communication_steps.rb +18 -2
  33. data/lib/kraken-mobile/runners/calabash/monkey/monkey_runner.rb +2 -2
  34. data/lib/kraken-mobile/steps/general_steps.rb +83 -0
  35. data/lib/kraken-mobile/steps/mobile/kraken_steps.rb +72 -0
  36. data/lib/kraken-mobile/steps/web/kraken_steps.rb +109 -0
  37. data/lib/kraken-mobile/test_scenario.rb +227 -0
  38. data/lib/kraken-mobile/utils/feature_reader.rb +17 -0
  39. data/lib/kraken-mobile/utils/k.rb +68 -0
  40. data/lib/kraken-mobile/utils/mobile_cucumber.rb +2 -0
  41. data/lib/kraken-mobile/utils/reporter.rb +500 -0
  42. data/lib/kraken-mobile/version.rb +2 -2
  43. data/lib/kraken-mobile/web/web_process.rb +41 -0
  44. data/lib/kraken_mobile.rb +81 -0
  45. data/reporter/assets/images/krakenThumbnail.jpg +0 -0
  46. data/reporter/feature_report.html.erb +5 -1
  47. data/reporter/index.html.erb +13 -7
  48. metadata +94 -13
  49. data/bin/kraken-mobile-calabash-android.rb +0 -85
  50. data/bin/kraken-mobile-generate.rb +0 -19
  51. data/bin/kraken-mobile-helpers.rb +0 -48
  52. data/bin/kraken-mobile-setup.rb +0 -50
  53. data/calabash-android-features-skeleton/step_definitions/kraken_steps.rb +0 -1
  54. data/lib/kraken-mobile.rb +0 -29
@@ -3,7 +3,7 @@ require 'kraken-mobile/helpers/feature_grouper'
3
3
  require 'kraken-mobile/runners/runner'
4
4
  require 'kraken-mobile/constants'
5
5
  require 'kraken-mobile/runners/calabash/android/apk_signer.rb'
6
- require 'kraken-mobile/runners/calabash/android/Operations'
6
+ require 'kraken-mobile/runners/calabash/android/operations'
7
7
  require 'kraken-mobile/runners/calabash/android/monkey_helper'
8
8
  require 'kraken-mobile/protocols/file_protocol'
9
9
  require 'parallel'
@@ -72,7 +72,7 @@ module KrakenMobile
72
72
  default_device = operations_module::Device.new(operations_module, ENV["ADB_DEVICE_ARG"], ENV["TEST_SERVER_PORT"], ENV["APP_PATH"], ENV["TEST_APP_PATH"])
73
73
  install_app_with_calabash
74
74
  default_device.start_test_server_in_background
75
- run_intelligent_monkey "#@user#{process_number}", 20
75
+ run_kraken_monkey "#@user#{process_number}", 20
76
76
  default_device.shutdown_test_server
77
77
  uninstall_app_with_calabash
78
78
  puts "\n****** PROCESS #{process_number} COMPLETED ******\n\n"
@@ -0,0 +1,83 @@
1
+ require 'kraken-mobile/helpers/kraken_faker'
2
+
3
+ ParameterType(
4
+ name: 'property',
5
+ regexp: /[^\"]*/,
6
+ type: String,
7
+ transformer: lambda do |string|
8
+ if string_is_a_property?(string)
9
+ string.slice!('<')
10
+ string.slice!('>')
11
+ handle_property(string)
12
+ elsif string_is_a_faker_reuse?(string)
13
+ handle_faker_reuse(string)
14
+ elsif string_is_a_faker?(string)
15
+ handle_faker(string)
16
+ else
17
+ return string
18
+ end
19
+ end
20
+ )
21
+
22
+ private
23
+
24
+ def current_process_id
25
+ tag_process_id = @scenario_tags.grep(/@user/).first
26
+ process_id = tag_process_id.delete_prefix('@user')
27
+ return 'ERROR: User not foud for scenario' if process_id.nil?
28
+
29
+ process_id
30
+ end
31
+
32
+ def string_is_a_property?(string)
33
+ string.start_with?('<') &&
34
+ string.end_with?('>')
35
+ end
36
+
37
+ def string_is_a_faker?(string)
38
+ string.start_with?('$')
39
+ end
40
+
41
+ def string_is_a_faker_reuse?(string)
42
+ string.start_with?('$$')
43
+ end
44
+
45
+ def handle_property(property)
46
+ properties = all_user_properties_as_json
47
+ process_id = current_process_id
48
+ user_id = "@user#{process_id}"
49
+
50
+ if !properties[user_id] || !properties[user_id][property]
51
+ raise "Property <#{property}> not found for @user#{current_process_id}"
52
+ end
53
+
54
+ properties[user_id][property]
55
+ end
56
+
57
+ def all_user_properties_as_json
58
+ raise 'ERROR: No properties file found' if ENV[K::PROPERTIES_PATH].nil?
59
+
60
+ properties_absolute_path = File.expand_path(ENV[K::PROPERTIES_PATH])
61
+ raise 'ERROR: Properties file not found' unless File.file?(
62
+ properties_absolute_path
63
+ )
64
+
65
+ file = open(properties_absolute_path)
66
+ content = file.read
67
+ file.close
68
+ JSON.parse(content)
69
+ end
70
+
71
+ def handle_faker(key)
72
+ faker = KrakenFaker.new(process_id: current_process_id)
73
+ faker.generate_value_for_key(
74
+ key: key
75
+ )
76
+ end
77
+
78
+ def handle_faker_reuse(key)
79
+ faker = KrakenFaker.new(process_id: current_process_id)
80
+ faker.reuse_value_for_key(
81
+ key: key
82
+ )
83
+ end
@@ -0,0 +1,72 @@
1
+ require 'calabash-android/calabash_steps'
2
+ require 'kraken-mobile/utils/k.rb'
3
+ require 'kraken-mobile/models/android_device'
4
+ require 'kraken-mobile/steps/general_steps'
5
+
6
+ Then(
7
+ /^I send a signal to user (\d+) containing "([^\"]*)"$/
8
+ ) do |process_id, signal|
9
+ device = Device.find_by_process_id(process_id)
10
+ raise 'ERROR: Device not found' if device.nil?
11
+ if process_id.to_s == current_process_id.to_s
12
+ raise 'ERROR: Can\'t send signal to same device'
13
+ end
14
+
15
+ device.write_signal(signal)
16
+ end
17
+
18
+ Then(/^I wait for a signal containing "([^\"]*)"$/) do |signal|
19
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
20
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
21
+
22
+ device = Device.find_by_process_id(current_process_id)
23
+ raise 'ERROR: Device not found' if device.nil?
24
+
25
+ device.read_signal(signal)
26
+ end
27
+
28
+ Then(
29
+ /^I wait for a signal containing "([^\"]*)" for (\d+) seconds$/
30
+ ) do |signal, seconds|
31
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
32
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
33
+
34
+ device = Device.find_by_process_id(current_process_id)
35
+ raise 'ERROR: Device not found' if device.nil?
36
+
37
+ device.read_signal(signal, seconds)
38
+ end
39
+
40
+ Then(/^I start a monkey with (\d+) events$/) do |number_of_events|
41
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
42
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
43
+
44
+ device = Device.find_by_process_id(current_process_id)
45
+ raise 'ERROR: Device not found' if device.nil?
46
+
47
+ device.run_monkey_with_number_of_events(number_of_events)
48
+ end
49
+
50
+ Then(/^I start kraken monkey with (\d+) events$/) do |number_of_events|
51
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
52
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
53
+
54
+ device = Device.find_by_process_id(current_process_id)
55
+ raise 'ERROR: Device not found' if device.nil?
56
+
57
+ device.run_kraken_monkey_with_number_of_events(number_of_events)
58
+ end
59
+
60
+ Then(/^I save device snapshot in file with path "([^\"]*)"$/) do |file_path|
61
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
62
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
63
+
64
+ device = Device.find_by_process_id(current_process_id)
65
+ raise 'ERROR: Device not found' if device.nil?
66
+
67
+ device.save_snapshot_in_file_path(file_path)
68
+ end
69
+
70
+ Then(/^I enter text "([^\"]*)"$/) do |text|
71
+ keyboard_enter_text(text, {})
72
+ end
@@ -0,0 +1,109 @@
1
+ require 'kraken-mobile/monkeys/web/web_monkey'
2
+ require 'kraken-mobile/steps/general_steps'
3
+ require 'kraken-mobile/utils/k.rb'
4
+ require 'selenium-webdriver'
5
+ require 'uri'
6
+
7
+ Before do
8
+ @driver = Selenium::WebDriver.for((ENV['BROWSER'] || 'chrome').to_sym)
9
+ end
10
+
11
+ Given(/^I navigate to page "([^\"]*)"$/) do |web_url|
12
+ raise 'ERROR: Invalid URL' if web_url.nil?
13
+ raise 'ERROR: Invalid URL' unless web_url =~ URI::DEFAULT_PARSER.make_regexp
14
+
15
+ @driver.navigate.to web_url
16
+ sleep 2
17
+ end
18
+
19
+ Then(/^I enter "([^\"]*)" into input field having id "([^\"]*)"$/) do |text, id|
20
+ @driver.find_element(:id, id).send_keys(text)
21
+ sleep 2
22
+ end
23
+
24
+ Then(
25
+ /^I enter "([^\"]*)" into input field having css selector "([^\"]*)"$/
26
+ ) do |text, selector|
27
+ @driver.find_element(:css, selector).send_keys(text)
28
+ sleep 2
29
+ end
30
+
31
+ Then(/^I click on element having id "(.*?)"$/) do |id|
32
+ @driver.find_element(:id, id).click
33
+ sleep 2
34
+ end
35
+
36
+ Then(/^I wait$/) do
37
+ sleep 5
38
+ end
39
+
40
+ Then(/^I wait for (\d+) seconds$/) do |seconds|
41
+ return if seconds.nil?
42
+
43
+ sleep seconds.to_i
44
+ end
45
+
46
+ Then(/^I should see text "(.*?)"$/) do |text|
47
+ @driver.page_source.include?(text)
48
+ end
49
+
50
+ Then(/^I click on element having css selector "(.*?)"$/) do |selector|
51
+ @driver.find_element(:css, selector).click
52
+ sleep 2
53
+ end
54
+
55
+ Then(
56
+ /^I select option with value "(.*?)" for dropdown with id "(.*?)"$/
57
+ ) do |op_value, sel_id|
58
+ drop = @driver.find_element(:id, sel_id)
59
+ choose = Selenium::WebDriver::Support::Select.new(drop)
60
+ choose.select_by(:value, op_value)
61
+ sleep 2
62
+ end
63
+
64
+ # Kraken Steps
65
+ Then(
66
+ /^I send a signal to user (\d+) containing "([^\"]*)"$/
67
+ ) do |process_id, signal|
68
+ device = Device.find_by_process_id(process_id)
69
+ raise 'ERROR: Device not found' if device.nil?
70
+ if process_id.to_s == current_process_id.to_s
71
+ raise 'ERROR: Can\'t send signal to same device'
72
+ end
73
+
74
+ device.write_signal(signal)
75
+ end
76
+
77
+ Then(/^I wait for a signal containing "([^\"]*)"$/) do |signal|
78
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
79
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
80
+
81
+ device = Device.find_by_process_id(current_process_id)
82
+ raise 'ERROR: Device not found' if device.nil?
83
+
84
+ device.read_signal(signal)
85
+ end
86
+
87
+ Then(
88
+ /^I wait for a signal containing "([^\"]*)" for (\d+) seconds$/
89
+ ) do |signal, seconds|
90
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.nil?
91
+ raise 'ERROR: Invalid scenario tag' if @scenario_tags.grep(/@user/).none?
92
+
93
+ device = Device.find_by_process_id(current_process_id)
94
+ raise 'ERROR: Device not found' if device.nil?
95
+
96
+ device.read_signal(signal, seconds)
97
+ end
98
+
99
+ Then(/^I start a monkey with (\d+) events$/) do |number_of_events|
100
+ monkey = WebMonkey.new(driver: @driver)
101
+ monkey.execute_kraken_monkey(number_of_events)
102
+ end
103
+
104
+ # Hooks
105
+ AfterStep do |_scenario|
106
+ path = "#{ENV[K::SCREENSHOT_PATH]}/#{SecureRandom.hex(12)}.png"
107
+ @driver.save_screenshot(path)
108
+ embed(path, 'image/png', File.basename(path))
109
+ end
@@ -0,0 +1,227 @@
1
+ require 'kraken-mobile/helpers/feature_analyzer'
2
+ require 'kraken-mobile/mobile/mobile_process'
3
+ require 'kraken-mobile/models/feature_file'
4
+ require 'kraken-mobile/models/web_device'
5
+ require 'kraken-mobile/web/web_process'
6
+ require 'kraken-mobile/utils/reporter'
7
+ require 'kraken-mobile/mobile/adb'
8
+ require 'kraken-mobile/utils/k.rb'
9
+ require 'parallel'
10
+
11
+ class TestScenario
12
+ #-------------------------------
13
+ # Fields
14
+ #-------------------------------
15
+ attr_accessor :devices
16
+ attr_accessor :kraken_app
17
+ attr_accessor :feature_file
18
+ attr_accessor :execution_id
19
+ attr_accessor :reporter
20
+
21
+ #-------------------------------
22
+ # Constructors
23
+ #-------------------------------
24
+ def initialize(kraken_app:, feature_file_path:)
25
+ @feature_file = FeatureFile.new(file_path: feature_file_path)
26
+ @devices = sample_devices
27
+ @kraken_app = kraken_app
28
+ @execution_id = Digest::SHA256.hexdigest(Time.now.to_f.to_s)
29
+ @reporter = Reporter.new(test_scenario: self)
30
+
31
+ ensure_apk_specified_if_necessary
32
+ end
33
+
34
+ #-------------------------------
35
+ # Lifecycle
36
+ #-------------------------------
37
+ def before_execution
38
+ setup_scenario_environment_variables
39
+ delete_all_web_inboxes
40
+ File.delete(K::DIRECTORY_PATH) if File.exist?(K::DIRECTORY_PATH)
41
+ File.delete(K::DICTIONARY_PATH) if File.exist?(K::DICTIONARY_PATH)
42
+ K::PROCESS_STATE_FILE_PATH.each do |_state, file_path|
43
+ File.delete(file_path) if File.exist?(file_path)
44
+ end
45
+ @reporter.create_report_folder_requirements
46
+ end
47
+
48
+ def run
49
+ unless @feature_file.right_syntax?
50
+ raise "ERROR: Verify feature file #{@file_path} has one unique @user tag"\
51
+ ' for each scenario'
52
+ end
53
+
54
+ before_execution
55
+ execute
56
+ after_execution
57
+ end
58
+
59
+ def after_execution
60
+ File.delete(K::DIRECTORY_PATH) if File.exist?(K::DIRECTORY_PATH)
61
+ File.delete(K::DICTIONARY_PATH) if File.exist?(K::DICTIONARY_PATH)
62
+ K::PROCESS_STATE_FILE_PATH.each do |_state, file_path|
63
+ File.delete(file_path) if File.exist?(file_path)
64
+ end
65
+ @reporter.save_report
66
+ notify_scenario_finished
67
+ end
68
+
69
+ #-------------------------------
70
+ # Methods
71
+ #-------------------------------
72
+ def execute
73
+ Parallel.map_with_index(
74
+ @devices, in_threads: @devices.count
75
+ ) do |device, index|
76
+ unless device.nil?
77
+ user_id = index + 1
78
+ start_process_for_user_id_in_device(
79
+ user_id, device
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ def self.ready_to_start?
86
+ process_ids = DeviceProcess.registered_process_ids
87
+ processes_ready = DeviceProcess.processes_in_state(
88
+ K::PROCESS_STATES[:ready_to_start]
89
+ )
90
+ process_ids.all? do |process_id|
91
+ processes_ready.include?(process_id) ||
92
+ Device.find_by_process_id(process_id)&.connected? == false
93
+ end
94
+ end
95
+
96
+ def self.ready_to_finish?
97
+ process_ids = DeviceProcess.registered_process_ids
98
+ processes_finished = DeviceProcess.processes_in_state(
99
+ K::PROCESS_STATES[:ready_to_finish]
100
+ )
101
+ process_ids.all? do |process_id|
102
+ processes_finished.include?(process_id) ||
103
+ Device.find_by_process_id(process_id)&.connected? == false
104
+ end
105
+ end
106
+
107
+ def requires_predefined_devices?
108
+ !ENV[K::CONFIG_PATH].nil?
109
+ end
110
+
111
+ private
112
+
113
+ def start_process_for_user_id_in_device(user_id, device)
114
+ if device.is_a? AndroidDevice
115
+ start_mobile_process_for_user_id_in_device(user_id, device)
116
+ elsif device.is_a? WebDevice
117
+ start_web_process_for_user_id_in_device(user_id, device)
118
+ else
119
+ raise 'ERROR: Platform not supported'
120
+ end
121
+ end
122
+
123
+ def start_mobile_process_for_user_id_in_device(user_id, device)
124
+ MobileProcess.new(
125
+ id: user_id,
126
+ device: device,
127
+ test_scenario: self
128
+ ).run
129
+ end
130
+
131
+ def start_web_process_for_user_id_in_device(user_id, device)
132
+ WebProcess.new(
133
+ id: user_id,
134
+ device: device,
135
+ test_scenario: self
136
+ ).run
137
+ end
138
+
139
+ def sample_devices
140
+ return predefined_devices if requires_predefined_devices?
141
+
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
147
+ end
148
+
149
+ def sample_mobile_devices
150
+ android_devices = ADB.connected_devices
151
+ android_devices.sample(
152
+ @feature_file.number_of_required_mobile_devices
153
+ )
154
+ end
155
+
156
+ def sample_web_devices
157
+ web_devices = []
158
+ @feature_file.number_of_required_web_devices.times do
159
+ web_devices << WebDevice.factory_create
160
+ end
161
+ web_devices
162
+ end
163
+
164
+ def notify_scenario_finished
165
+ @kraken_app.on_test_scenario_finished
166
+ end
167
+
168
+ def user_id_is_mobile?(user_id)
169
+ complement_tags = @feature_file.tags_for_user_id(user_id).map(
170
+ &:downcase
171
+ )
172
+ complement_tags.include?('@mobile')
173
+ end
174
+
175
+ def user_id_is_web?(user_id)
176
+ complement_tags = @feature_file.tags_for_user_id(user_id).map(
177
+ &:downcase
178
+ )
179
+ complement_tags.include?('@web')
180
+ end
181
+
182
+ def delete_all_web_inboxes
183
+ Dir.glob(".*_#{K::INBOX_FILE_NAME}").each do |file|
184
+ File.delete(file)
185
+ end
186
+ end
187
+
188
+ def predefined_devices
189
+ config_absolute_path = File.expand_path(ENV[K::CONFIG_PATH])
190
+ file = open(config_absolute_path)
191
+ content = file.read
192
+ file.close
193
+ devices_json = JSON.parse(content).values
194
+ devices_json.map do |device_json|
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
206
+ end
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
227
+ end