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.
- 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,135 @@
|
|
1
|
+
require 'gherkin/parser'
|
2
|
+
require 'gherkin/pickles/compiler'
|
3
|
+
require 'kraken-mobile/models/feature_scenario'
|
4
|
+
|
5
|
+
class FeatureFile
|
6
|
+
#-------------------------------
|
7
|
+
# Fields
|
8
|
+
#-------------------------------
|
9
|
+
attr_accessor :file_path
|
10
|
+
attr_accessor :scenarios
|
11
|
+
|
12
|
+
#-------------------------------
|
13
|
+
# Constructos
|
14
|
+
#-------------------------------
|
15
|
+
def initialize(file_path:)
|
16
|
+
@file_path = file_path
|
17
|
+
@scenarios = []
|
18
|
+
|
19
|
+
read_content
|
20
|
+
end
|
21
|
+
|
22
|
+
#-------------------------------
|
23
|
+
# Helpers
|
24
|
+
#-------------------------------
|
25
|
+
def user_tags
|
26
|
+
all_tags = @scenarios.map(&:tags).flatten.uniq
|
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'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def number_of_required_mobile_devices
|
41
|
+
system_tags.select { |tag| tag == '@mobile' }.count
|
42
|
+
end
|
43
|
+
|
44
|
+
def number_of_required_web_devices
|
45
|
+
system_tags.select { |tag| tag == '@web' }.count
|
46
|
+
end
|
47
|
+
|
48
|
+
def number_of_required_devices
|
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
|
68
|
+
end
|
69
|
+
|
70
|
+
def tags_for_user_id(user_id)
|
71
|
+
user_tag = "@user#{user_id}"
|
72
|
+
user_scenario = @scenarios.select do |scenario|
|
73
|
+
scenario.tags.include?(user_tag)
|
74
|
+
end.first
|
75
|
+
return [] if user_scenario.nil? || user_scenario.tags.nil?
|
76
|
+
|
77
|
+
user_scenario.tags.reject { |tag| tag == user_tag }
|
78
|
+
end
|
79
|
+
|
80
|
+
def right_syntax?
|
81
|
+
all_scenarios_have_a_user_tag? &&
|
82
|
+
only_one_user_tag_for_each_scenario? &&
|
83
|
+
!duplicate_tags_for_a_user?
|
84
|
+
end
|
85
|
+
|
86
|
+
def duplicate_tags_for_a_user?
|
87
|
+
taken_user_tags = {}
|
88
|
+
scenarios.each do |scenario|
|
89
|
+
user_tag = scenario.tags.select do |tag|
|
90
|
+
tag.start_with?('@user')
|
91
|
+
end.first
|
92
|
+
return true unless taken_user_tags[user_tag].nil?
|
93
|
+
|
94
|
+
taken_user_tags[user_tag] = user_tag
|
95
|
+
end
|
96
|
+
false
|
97
|
+
end
|
98
|
+
|
99
|
+
def only_one_user_tag_for_each_scenario?
|
100
|
+
scenarios.each do |scenario|
|
101
|
+
user_tags = scenario.tags.select do |tag|
|
102
|
+
tag.start_with?('@user')
|
103
|
+
end
|
104
|
+
return false if user_tags.count != 1
|
105
|
+
end
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def all_scenarios_have_a_user_tag?
|
110
|
+
scenarios.each do |scenario|
|
111
|
+
user_tag = scenario.tags.select do |tag|
|
112
|
+
tag.start_with?('@user')
|
113
|
+
end.first
|
114
|
+
return false if user_tag.nil?
|
115
|
+
end
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def read_content
|
122
|
+
parser = Gherkin::Parser.new
|
123
|
+
file = File.open(file_path)
|
124
|
+
file_content = file.read
|
125
|
+
file.close
|
126
|
+
gherkin_document = parser.parse(file_content)
|
127
|
+
pickles = Gherkin::Pickles::Compiler.new.compile(gherkin_document)
|
128
|
+
pickles.each do |scenario|
|
129
|
+
scenarios << FeatureScenario.new(
|
130
|
+
name: scenario[:name],
|
131
|
+
tags: scenario[:tags]
|
132
|
+
)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class FeatureScenario
|
2
|
+
#-------------------------------
|
3
|
+
# Fields
|
4
|
+
#-------------------------------
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :tags
|
7
|
+
|
8
|
+
#-------------------------------
|
9
|
+
# Constructos
|
10
|
+
#-------------------------------
|
11
|
+
def initialize(name:, tags:)
|
12
|
+
@name = name
|
13
|
+
@tags = format_tags(tags) || []
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
#-------------------------------
|
19
|
+
# Helpers
|
20
|
+
#-------------------------------
|
21
|
+
def format_tags(tags)
|
22
|
+
tags.map { |tag| tag[:name] }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'kraken-mobile/models/device'
|
2
|
+
|
3
|
+
class WebDevice < Device
|
4
|
+
#-------------------------------
|
5
|
+
# Signaling
|
6
|
+
#-------------------------------
|
7
|
+
def create_inbox
|
8
|
+
file = File.open(inbox_file_path, 'w')
|
9
|
+
file.close
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete_inbox
|
13
|
+
return unless File.exist? inbox_file_path
|
14
|
+
|
15
|
+
File.delete(inbox_file_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_signal(signal)
|
19
|
+
File.open(inbox_file_path, 'a') do |file|
|
20
|
+
file.puts(signal)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def read_signal(signal, timeout = K::DEFAULT_TIMEOUT_SECONDS)
|
25
|
+
Timeout.timeout(timeout, RuntimeError) do
|
26
|
+
sleep(1) until inbox_last_signal == signal
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#-------------------------------
|
31
|
+
# More interface methods
|
32
|
+
#-------------------------------
|
33
|
+
def connected?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def orientation
|
38
|
+
K::WEB_PORTRAIT
|
39
|
+
end
|
40
|
+
|
41
|
+
def screen_size
|
42
|
+
height = 0
|
43
|
+
width = 0
|
44
|
+
|
45
|
+
[height, width]
|
46
|
+
end
|
47
|
+
|
48
|
+
def sdk_version
|
49
|
+
1.0 # Default
|
50
|
+
end
|
51
|
+
|
52
|
+
def type
|
53
|
+
K::WEB_DEVICE
|
54
|
+
end
|
55
|
+
|
56
|
+
#-------------------------------
|
57
|
+
# Random testing
|
58
|
+
#-------------------------------
|
59
|
+
def run_monkey_with_number_of_events(number_of_events)
|
60
|
+
number_of_events # TODO, implement
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_kraken_monkey_with_number_of_events(number_of_events)
|
64
|
+
number_of_events # TODO, implement
|
65
|
+
end
|
66
|
+
|
67
|
+
#-------------------------------
|
68
|
+
# Helpers
|
69
|
+
#-------------------------------
|
70
|
+
def self.factory_create
|
71
|
+
WebDevice.new(
|
72
|
+
id: SecureRandom.hex(10),
|
73
|
+
model: 'Web'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def inbox_file_path
|
80
|
+
".#{@id}_#{K::INBOX_FILE_NAME}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def inbox_last_signal
|
84
|
+
file = File.open(inbox_file_path)
|
85
|
+
lines = file.to_a
|
86
|
+
file.close
|
87
|
+
lines.last&.strip
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'calabash-android/environment_helpers.rb'
|
2
|
+
require 'calabash-android/monkey_helpers'
|
3
|
+
require 'calabash-android/operations'
|
4
|
+
|
5
|
+
module AndroidMonkey
|
6
|
+
include Calabash::Android::Operations
|
7
|
+
include Calabash::Android::MonkeyHelpers
|
8
|
+
|
9
|
+
def execute_monkey(number_of_events)
|
10
|
+
height, width = screen_size
|
11
|
+
start_monkey
|
12
|
+
|
13
|
+
number_of_events.times do |_i|
|
14
|
+
monkey_touch(
|
15
|
+
K::CALABASH_MONKEY_ACTIONS.sample,
|
16
|
+
rand(5..(width - 5)),
|
17
|
+
rand(5..(height - 5))
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
kill_existing_monkey_processes
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Override calabash super adb_command method
|
27
|
+
def adb_command
|
28
|
+
calabash_default_device.adb_command
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module KrakenAndroidMonkey
|
2
|
+
def execute_kraken_monkey(number_of_events)
|
3
|
+
number_of_events.times do |_i|
|
4
|
+
execute_random_action
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute_random_action
|
9
|
+
Timeout.timeout(K::MONKEY_DEFAULT_TIMEOUT, RuntimeError) do
|
10
|
+
begin
|
11
|
+
arr = [
|
12
|
+
method(:random_click), method(:insert_random_text)
|
13
|
+
]
|
14
|
+
arr.sample.call
|
15
|
+
rescue StandardError => _e
|
16
|
+
puts 'ERROR: Kraken monkey couldn\'t perfom action'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Actions
|
22
|
+
private
|
23
|
+
|
24
|
+
def random_click
|
25
|
+
elements = query('*')
|
26
|
+
return if elements.nil?
|
27
|
+
return if elements.none?
|
28
|
+
|
29
|
+
element = elements.sample
|
30
|
+
return if element['rect'].nil?
|
31
|
+
|
32
|
+
x = element['rect']['x']
|
33
|
+
y = element['rect']['y']
|
34
|
+
perform_action('touch_coordinate', x, y)
|
35
|
+
end
|
36
|
+
|
37
|
+
def insert_random_text
|
38
|
+
inputs = query('android.support.v7.widget.AppCompatEditText')
|
39
|
+
return if inputs.nil?
|
40
|
+
return if inputs.none?
|
41
|
+
|
42
|
+
input = inputs.sample
|
43
|
+
return if input['rect'].nil?
|
44
|
+
|
45
|
+
x = input['rect']['x']
|
46
|
+
y = input['rect']['y']
|
47
|
+
perform_action('touch_coordinate', x, y)
|
48
|
+
enter_text SecureRandom.hex
|
49
|
+
end
|
50
|
+
|
51
|
+
def input_texts
|
52
|
+
query('android.support.v7.widget.AppCompatEditText')
|
53
|
+
end
|
54
|
+
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
|
@@ -5,8 +5,8 @@ module KrakenMobile
|
|
5
5
|
module CalabashAndroid
|
6
6
|
module MonkeyHelper
|
7
7
|
|
8
|
-
# Runs
|
9
|
-
def
|
8
|
+
# Runs kraken monkey
|
9
|
+
def run_kraken_monkey channel, number_of_events
|
10
10
|
device_id = channel_to_device_id channel
|
11
11
|
logger = open("./#{device_id}.txt", 'w')
|
12
12
|
number_of_events.times do |i|
|
@@ -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]
|
@@ -18,6 +19,21 @@ ParameterType(
|
|
18
19
|
}
|
19
20
|
)
|
20
21
|
|
22
|
+
ParameterType(
|
23
|
+
name: 'property',
|
24
|
+
regexp: /[^\"]*/,
|
25
|
+
type: String,
|
26
|
+
transformer: ->(s) {
|
27
|
+
channel = @scenario_tags.grep(/@user/).first
|
28
|
+
if ENV["PROPERTIES_PATH"] && channel && s.start_with?("<") && s.end_with?(">")
|
29
|
+
s.slice!('$')
|
30
|
+
return properties[channel][s]
|
31
|
+
else
|
32
|
+
return s
|
33
|
+
end
|
34
|
+
}
|
35
|
+
)
|
36
|
+
|
21
37
|
Then /^I wait for a signal containing "([^\"]*)"$/ do |string|
|
22
38
|
channel = @scenario_tags.grep(/@user/).first
|
23
39
|
readSignal(channel, string, KrakenMobile::Constants::DEFAULT_TIMEOUT)
|
@@ -46,9 +62,9 @@ Then /^I start a monkey with (\d+) events$/ do |int|
|
|
46
62
|
run_monkey channel, int
|
47
63
|
end
|
48
64
|
|
49
|
-
Then /^I start
|
65
|
+
Then /^I start kraken monkey with (\d+) events$/ do |int|
|
50
66
|
channel = @scenario_tags.grep(/@user/).first
|
51
|
-
|
67
|
+
run_kraken_monkey channel, int
|
52
68
|
end
|
53
69
|
|
54
70
|
Then /^I start a monkey with (\d+) events from height (\d+)% to (\d+)% and width (\d+)% to (\d+)%$/ do |int, from_x, to_x, from_y, to_y|
|