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.
- checksums.yaml +5 -5
- data/README.md +251 -75
- data/bin/kraken-mobile +64 -67
- data/bin/kraken_mobile_calabash_android.rb +39 -0
- data/bin/kraken_mobile_helpers.rb +107 -0
- data/bin/kraken_mobile_setup.rb +138 -0
- data/calabash-android-features-skeleton/step_definitions/mobile_steps.rb +1 -0
- data/calabash-android-features-skeleton/support/app_installation_hooks.rb +2 -0
- data/calabash-android-features-skeleton/support/app_life_cycle_hooks.rb +3 -4
- data/calabash-android-features-skeleton/support/env.rb +1 -1
- data/calabash-android-features-skeleton/web/step_definitions/web_steps.rb +3 -0
- data/calabash-android-features-skeleton/web/support/app_life_cycle_hooks.rb +15 -0
- data/lib/kraken-mobile/device_process.rb +130 -0
- data/lib/kraken-mobile/helpers/devices_helper/adb_helper.rb +100 -105
- data/lib/kraken-mobile/helpers/kraken_faker.rb +108 -0
- data/lib/kraken-mobile/helpers/reporter.rb +3 -0
- data/lib/kraken-mobile/hooks/mobile_kraken_hooks.rb +15 -0
- data/lib/kraken-mobile/hooks/mobile_operations.rb +36 -0
- data/lib/kraken-mobile/hooks/web_operations.rb +33 -0
- data/lib/kraken-mobile/mobile/adb.rb +66 -0
- data/lib/kraken-mobile/mobile/android_commands.rb +43 -0
- data/lib/kraken-mobile/mobile/mobile_process.rb +101 -0
- data/lib/kraken-mobile/models/android_device.rb +121 -0
- data/lib/kraken-mobile/models/device.rb +113 -31
- data/lib/kraken-mobile/models/feature_file.rb +135 -0
- data/lib/kraken-mobile/models/feature_scenario.rb +24 -0
- data/lib/kraken-mobile/models/web_device.rb +89 -0
- data/lib/kraken-mobile/monkeys/mobile/android_monkey.rb +30 -0
- data/lib/kraken-mobile/monkeys/mobile/kraken_android_monkey.rb +54 -0
- data/lib/kraken-mobile/monkeys/web/web_monkey.rb +63 -0
- data/lib/kraken-mobile/runners/calabash/android/monkey_helper.rb +2 -2
- data/lib/kraken-mobile/runners/calabash/android/steps/communication_steps.rb +18 -2
- data/lib/kraken-mobile/runners/calabash/monkey/monkey_runner.rb +2 -2
- data/lib/kraken-mobile/steps/general_steps.rb +83 -0
- data/lib/kraken-mobile/steps/mobile/kraken_steps.rb +72 -0
- data/lib/kraken-mobile/steps/web/kraken_steps.rb +109 -0
- data/lib/kraken-mobile/test_scenario.rb +227 -0
- data/lib/kraken-mobile/utils/feature_reader.rb +17 -0
- data/lib/kraken-mobile/utils/k.rb +68 -0
- data/lib/kraken-mobile/utils/mobile_cucumber.rb +2 -0
- data/lib/kraken-mobile/utils/reporter.rb +500 -0
- data/lib/kraken-mobile/version.rb +2 -2
- data/lib/kraken-mobile/web/web_process.rb +41 -0
- data/lib/kraken_mobile.rb +81 -0
- data/reporter/assets/images/krakenThumbnail.jpg +0 -0
- data/reporter/feature_report.html.erb +5 -1
- data/reporter/index.html.erb +13 -7
- metadata +94 -13
- data/bin/kraken-mobile-calabash-android.rb +0 -85
- data/bin/kraken-mobile-generate.rb +0 -19
- data/bin/kraken-mobile-helpers.rb +0 -48
- data/bin/kraken-mobile-setup.rb +0 -50
- data/calabash-android-features-skeleton/step_definitions/kraken_steps.rb +0 -1
- data/lib/kraken-mobile.rb +0 -29
@@ -0,0 +1,39 @@
|
|
1
|
+
@features_dir = File.join(FileUtils.pwd, 'features')
|
2
|
+
@support_dir = File.join(@features_dir, 'support')
|
3
|
+
@source_dir = File.join(
|
4
|
+
File.dirname(__FILE__),
|
5
|
+
'..',
|
6
|
+
'calabash-android-features-skeleton'
|
7
|
+
)
|
8
|
+
|
9
|
+
def handle_calabash_android(config_path, properties_path)
|
10
|
+
require 'kraken-mobile/constants'
|
11
|
+
require 'calabash-android/helpers'
|
12
|
+
|
13
|
+
kraken = KrakenApp.new(
|
14
|
+
apk_path: user_entered_apk_path,
|
15
|
+
properties_path: format_properties(properties_path),
|
16
|
+
config_path: format_config(config_path)
|
17
|
+
)
|
18
|
+
kraken.start
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def format_properties(properties_path)
|
24
|
+
return if properties_path.nil?
|
25
|
+
|
26
|
+
properties_absolute_path = File.expand_path(properties_path)
|
27
|
+
ensure_properties_is_valid(properties_absolute_path)
|
28
|
+
|
29
|
+
properties_absolute_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def format_config(config_path)
|
33
|
+
return if config_path.nil?
|
34
|
+
|
35
|
+
config_absolute_path = File.expand_path(config_path)
|
36
|
+
ensure_configuration_is_valid(config_absolute_path)
|
37
|
+
|
38
|
+
config_absolute_path
|
39
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
#-------------------------------
|
5
|
+
# Helpers
|
6
|
+
#-------------------------------
|
7
|
+
|
8
|
+
def print_usage
|
9
|
+
puts <<USAGE
|
10
|
+
Usage: kraken-mobile <command-name> [parameters] [options]
|
11
|
+
<command-name> can be one of
|
12
|
+
run <apk>
|
13
|
+
runs Cucumber in the current folder with the environment needed.
|
14
|
+
version
|
15
|
+
prints the gem version.
|
16
|
+
devices
|
17
|
+
prints the list of devices attached.
|
18
|
+
setup
|
19
|
+
creates kraken-settings file specifying in what devices the tests are going to be run.
|
20
|
+
gen
|
21
|
+
generate a features folder structure.
|
22
|
+
resign <apk>
|
23
|
+
resigns the app with the currently configured keystore.
|
24
|
+
USAGE
|
25
|
+
end
|
26
|
+
|
27
|
+
def print_devices
|
28
|
+
puts 'List of devices attached'
|
29
|
+
ADB.connected_devices.each_with_index do |device, index|
|
30
|
+
puts "user#{index + 1} - #{device.to_s.split(K::SEPARATOR).join(' - ')}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def apk_file?(file_path)
|
35
|
+
file_path.end_with?('.apk') && File.exist?(file_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def relative_to_full_path(file_path)
|
39
|
+
File.expand_path(file_path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def msg(title, &block)
|
43
|
+
puts "\n" + '-' * 10 + title + '-' * 10
|
44
|
+
block.call
|
45
|
+
puts '-' * 10 + '-------' + '-' * 10 + "\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
def scaffold_folder_structure
|
49
|
+
if File.exist?(@features_dir)
|
50
|
+
puts 'A features directory already exists. Stopping...'
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
msg('Question') do
|
55
|
+
puts 'I\'m about to create a subdirectory called features.'
|
56
|
+
puts 'features will contain all your kraken tests.'
|
57
|
+
puts 'Please hit return to confirm that\'s what you want.'
|
58
|
+
end
|
59
|
+
exit 2 unless STDIN.gets.chomp == ''
|
60
|
+
|
61
|
+
FileUtils.cp_r(@source_dir, @features_dir)
|
62
|
+
|
63
|
+
msg('Info') do
|
64
|
+
puts "features subdirectory created. \n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def ensure_apk_is_specified
|
69
|
+
return if !ARGV.empty? && apk_file?(ARGV.first)
|
70
|
+
|
71
|
+
puts 'The first parameter must be the path to a valid apk file.'
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def ensure_configuration_is_valid(configuration)
|
76
|
+
return if File.exist?(configuration) &&
|
77
|
+
File.file?(configuration) &&
|
78
|
+
configuration.end_with?('.json')
|
79
|
+
|
80
|
+
puts 'The path of the configuration file is not valid.'
|
81
|
+
exit 1
|
82
|
+
end
|
83
|
+
|
84
|
+
def ensure_properties_is_valid(properties)
|
85
|
+
return if File.exist?(properties) &&
|
86
|
+
File.file?(properties) &&
|
87
|
+
properties.end_with?('.json')
|
88
|
+
|
89
|
+
puts 'The path of the properties file is not valid.'
|
90
|
+
exit 1
|
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
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'tty-prompt'
|
3
|
+
$LOAD_PATH << File.expand_path('../lib', __dir__)
|
4
|
+
require 'kraken-mobile/utils/k.rb'
|
5
|
+
|
6
|
+
class KrakenSetup
|
7
|
+
WEB_IDENTIFIER = 'Web'.freeze
|
8
|
+
IS_WEB_AVAILABLE_FOR_SELECTION = true
|
9
|
+
|
10
|
+
attr_accessor :prompt
|
11
|
+
attr_accessor :devices_connected_id
|
12
|
+
attr_accessor :settings
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@prompt = TTY::Prompt.new
|
16
|
+
@available_devices_ids = available_devices_ids_for_select
|
17
|
+
@settings = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
number_of_devices_required.times do |index|
|
22
|
+
user_id = index + 1
|
23
|
+
selected_device_id = select_device_for_user_with_id(user_id)
|
24
|
+
if selected_device_id == WEB_IDENTIFIER
|
25
|
+
handle_web_setup_for_user_with_id(user_id)
|
26
|
+
else
|
27
|
+
handle_mobile_setup_for_user_with_id(user_id, selected_device_id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
save_user_settings_to_file
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def handle_web_setup_for_user_with_id(user_id)
|
36
|
+
save_web_device_settings_for_user_with_id(user_id: user_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_mobile_setup_for_user_with_id(user_id, device_id)
|
40
|
+
selected_apk = select_apk_for_user_with_id(user_id)
|
41
|
+
save_mobile_device_settings_for_user_with_id(
|
42
|
+
user_id: user_id, apk: selected_apk,
|
43
|
+
device_id: device_id
|
44
|
+
)
|
45
|
+
delete_selected_device_id_from_available_devices(device_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
def save_user_settings_to_file
|
49
|
+
open('kraken_mobile_settings.json', 'w') do |f|
|
50
|
+
f.puts @settings.to_json
|
51
|
+
end
|
52
|
+
|
53
|
+
puts 'Saved your settings to .kraken_mobile_settings. You can edit '\
|
54
|
+
'the settings manually or run this setup script again'
|
55
|
+
end
|
56
|
+
|
57
|
+
def number_of_devices_required
|
58
|
+
number_of_devices = -1
|
59
|
+
max_devices = max_number_of_devices
|
60
|
+
while number_of_devices <= 0 || number_of_devices > max_number_of_devices
|
61
|
+
number_of_devices = @prompt.ask(
|
62
|
+
'How many devices do you want to use?', convert: :int
|
63
|
+
)
|
64
|
+
|
65
|
+
if number_of_devices.zero?
|
66
|
+
@prompt.error('Number of devices can\'t be less than 1.')
|
67
|
+
end
|
68
|
+
|
69
|
+
if number_of_devices > max_devices
|
70
|
+
@prompt.error(
|
71
|
+
"You can run Kraken only in #{max_devices} device" \
|
72
|
+
"#{max_devices > 1 ? 's' : ''}"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
number_of_devices
|
77
|
+
end
|
78
|
+
|
79
|
+
def delete_selected_device_id_from_available_devices(device_id)
|
80
|
+
@available_devices_ids.delete(device_id)
|
81
|
+
end
|
82
|
+
|
83
|
+
def save_mobile_device_settings_for_user_with_id(
|
84
|
+
user_id:, apk:, device_id:
|
85
|
+
)
|
86
|
+
device = ADB.connected_devices.select { |d| d.id == device_id }.first
|
87
|
+
@settings[user_id] = {
|
88
|
+
id: device_id,
|
89
|
+
model: device.model,
|
90
|
+
type: K::ANDROID_DEVICE,
|
91
|
+
config: {
|
92
|
+
apk_path: apk
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def save_web_device_settings_for_user_with_id(user_id:)
|
98
|
+
device = WebDevice.factory_create
|
99
|
+
@settings[user_id] = {
|
100
|
+
id: device.id,
|
101
|
+
model: device.model,
|
102
|
+
type: K::WEB_DEVICE,
|
103
|
+
config: {}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def select_apk_for_user_with_id(id)
|
108
|
+
valid_apk = false
|
109
|
+
|
110
|
+
until valid_apk
|
111
|
+
entered_text = @prompt.ask(
|
112
|
+
"What APK user #{id} is going to run?", required: true
|
113
|
+
)
|
114
|
+
entered_apk_path = File.expand_path(entered_text)
|
115
|
+
valid_apk = apk_file?(File.expand_path(entered_apk_path))
|
116
|
+
@prompt.error('APK path is not valid.') unless valid_apk
|
117
|
+
end
|
118
|
+
entered_apk_path
|
119
|
+
end
|
120
|
+
|
121
|
+
def select_device_for_user_with_id(id)
|
122
|
+
@prompt.select(
|
123
|
+
"Choose your user #{id}", @available_devices_ids
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
def available_devices_ids_for_select
|
128
|
+
devices_connected_id = ADB.connected_devices.map(&:id)
|
129
|
+
devices_connected_id << WEB_IDENTIFIER if IS_WEB_AVAILABLE_FOR_SELECTION
|
130
|
+
devices_connected_id
|
131
|
+
end
|
132
|
+
|
133
|
+
def max_number_of_devices
|
134
|
+
return 100 if IS_WEB_AVAILABLE_FOR_SELECTION
|
135
|
+
|
136
|
+
ADB.connected_devices.count
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'kraken-mobile/steps/mobile/kraken_steps'
|
@@ -1,10 +1,9 @@
|
|
1
|
-
require 'kraken-mobile/
|
1
|
+
require 'kraken-mobile/hooks/mobile_operations'
|
2
2
|
|
3
3
|
Before do |scenario|
|
4
|
-
|
4
|
+
start_test_kraken_server_in_background
|
5
5
|
end
|
6
6
|
|
7
7
|
After do |scenario|
|
8
|
-
|
9
|
-
uninstall_app_with_calabash
|
8
|
+
shutdown_test_kraken_server
|
10
9
|
end
|
@@ -1 +1 @@
|
|
1
|
-
require 'kraken-mobile/
|
1
|
+
require 'kraken-mobile/utils/mobile_cucumber'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
if ENV["ADB_DEVICE_ARG"].nil?
|
2
|
+
require 'kraken-mobile/hooks/web_operations'
|
3
|
+
|
4
|
+
Before do |scenario|
|
5
|
+
start_test_kraken_server_in_background(
|
6
|
+
scenario: scenario
|
7
|
+
)
|
8
|
+
end
|
9
|
+
|
10
|
+
After do |scenario|
|
11
|
+
shutdown_test_kraken_server(
|
12
|
+
scenario: scenario
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
# Abstract class
|
4
|
+
class DeviceProcess
|
5
|
+
attr_accessor :id
|
6
|
+
attr_accessor :device
|
7
|
+
attr_accessor :test_scenario
|
8
|
+
|
9
|
+
#-------------------------------
|
10
|
+
# Constructors
|
11
|
+
#-------------------------------
|
12
|
+
def initialize(id:, device:, test_scenario:)
|
13
|
+
@id = id
|
14
|
+
@device = device
|
15
|
+
@test_scenario = test_scenario
|
16
|
+
end
|
17
|
+
|
18
|
+
#-------------------------------
|
19
|
+
# Required methods
|
20
|
+
#-------------------------------
|
21
|
+
def before_execute
|
22
|
+
raise 'ERROR: before_execute not implemented.'
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute
|
26
|
+
raise 'ERROR: execute not implemented.'
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_execute
|
30
|
+
raise 'ERROR: after_execute not implemented.'
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
before_execute
|
35
|
+
execute
|
36
|
+
after_execute
|
37
|
+
end
|
38
|
+
|
39
|
+
#-------------------------------
|
40
|
+
# Methods
|
41
|
+
#-------------------------------
|
42
|
+
def register_process_to_directory
|
43
|
+
File.open(K::DIRECTORY_PATH, 'a') do |file|
|
44
|
+
file.puts("#{id}#{K::SEPARATOR}#{device}")
|
45
|
+
end
|
46
|
+
end
|
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
|
+
|
61
|
+
def notify_ready_to_start
|
62
|
+
File.open(K::DIRECTORY_PATH, 'a') do |file|
|
63
|
+
file.puts("#{id}#{K::SEPARATOR}#{device}")
|
64
|
+
end
|
65
|
+
end
|
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
|
+
|
82
|
+
def self.directory
|
83
|
+
return [] unless File.exist?(K::DIRECTORY_PATH)
|
84
|
+
|
85
|
+
directory = nil
|
86
|
+
File.open(K::DIRECTORY_PATH, 'r') do |file|
|
87
|
+
directory = file.each_line.map(&:to_s).map(&:strip)
|
88
|
+
end
|
89
|
+
|
90
|
+
directory || []
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.registered_process_ids
|
94
|
+
directory = DeviceProcess.directory
|
95
|
+
directory.map do |entry|
|
96
|
+
info = entry.strip.split(K::SEPARATOR)
|
97
|
+
info[0]
|
98
|
+
end.compact.uniq
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.notify_process_state(process_id:, state:)
|
102
|
+
raise 'ERROR: Process id can\'t be nil.' if process_id.nil?
|
103
|
+
|
104
|
+
file_path = K::PROCESS_STATE_FILE_PATH[state]
|
105
|
+
raise 'ERROR: State does not exist.' if file_path.nil?
|
106
|
+
|
107
|
+
File.open(file_path, 'a') do |file|
|
108
|
+
file.puts(process_id)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.processes_in_state(state)
|
113
|
+
file_path = K::PROCESS_STATE_FILE_PATH[state]
|
114
|
+
return [] if file_path.nil?
|
115
|
+
return [] unless File.exist?(file_path)
|
116
|
+
|
117
|
+
devices_ready = nil
|
118
|
+
File.open(file_path, 'r') do |file|
|
119
|
+
devices_ready = file.each_line.map(&:to_s).map(&:strip)
|
120
|
+
end
|
121
|
+
|
122
|
+
devices_ready || []
|
123
|
+
end
|
124
|
+
|
125
|
+
def terminal_command_separator
|
126
|
+
return ' & ' if running_on_windows?
|
127
|
+
|
128
|
+
';'
|
129
|
+
end
|
130
|
+
end
|