kraken-mobile 1.0.2 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
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