kraken-mobile 1.0.4 → 1.0.5

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -11
  3. data/bin/kraken-mobile +55 -67
  4. data/bin/kraken_mobile_calabash_android.rb +39 -0
  5. data/bin/kraken_mobile_helpers.rb +91 -0
  6. data/bin/kraken_mobile_setup.rb +134 -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 +94 -0
  14. data/lib/kraken-mobile/helpers/devices_helper/adb_helper.rb +100 -105
  15. data/lib/kraken-mobile/helpers/kraken_faker.rb +107 -0
  16. data/lib/kraken-mobile/hooks/mobile_kraken_hooks.rb +15 -0
  17. data/lib/kraken-mobile/hooks/mobile_operations.rb +36 -0
  18. data/lib/kraken-mobile/hooks/web_operations.rb +33 -0
  19. data/lib/kraken-mobile/mobile/adb.rb +66 -0
  20. data/lib/kraken-mobile/mobile/android_commands.rb +43 -0
  21. data/lib/kraken-mobile/mobile/mobile_process.rb +82 -0
  22. data/lib/kraken-mobile/models/android_device.rb +121 -0
  23. data/lib/kraken-mobile/models/device.rb +113 -31
  24. data/lib/kraken-mobile/models/feature_file.rb +108 -0
  25. data/lib/kraken-mobile/models/feature_scenario.rb +24 -0
  26. data/lib/kraken-mobile/models/web_device.rb +86 -0
  27. data/lib/kraken-mobile/monkeys/mobile/android_monkey.rb +30 -0
  28. data/lib/kraken-mobile/monkeys/mobile/kraken_android_monkey.rb +54 -0
  29. data/lib/kraken-mobile/runners/calabash/android/steps/communication_steps.rb +15 -0
  30. data/lib/kraken-mobile/steps/general_steps.rb +82 -0
  31. data/lib/kraken-mobile/steps/mobile/kraken_steps.rb +72 -0
  32. data/lib/kraken-mobile/steps/web/kraken_steps.rb +81 -0
  33. data/lib/kraken-mobile/test_scenario.rb +193 -0
  34. data/lib/kraken-mobile/utils/feature_reader.rb +17 -0
  35. data/lib/kraken-mobile/utils/k.rb +67 -0
  36. data/lib/kraken-mobile/utils/mobile_cucumber.rb +2 -0
  37. data/lib/kraken-mobile/utils/reporter.rb +453 -0
  38. data/lib/kraken-mobile/version.rb +2 -2
  39. data/lib/kraken-mobile/web/web_process.rb +39 -0
  40. data/lib/kraken_mobile.rb +77 -0
  41. data/reporter/index.html.erb +6 -6
  42. metadata +89 -9
  43. data/bin/kraken-mobile-calabash-android.rb +0 -85
  44. data/bin/kraken-mobile-generate.rb +0 -19
  45. data/bin/kraken-mobile-helpers.rb +0 -48
  46. data/bin/kraken-mobile-setup.rb +0 -50
  47. data/calabash-android-features-skeleton/step_definitions/kraken_steps.rb +0 -1
  48. data/lib/kraken-mobile.rb +0 -29
@@ -0,0 +1,108 @@
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 number_of_required_mobile_devices
26
+ all_tags = @scenarios.map(&:tags).flatten.uniq
27
+ mobile_tagged_count = all_tags.select { |tag| tag == '@mobile' }.count
28
+ empty_tagged_scenarios = @scenarios.select do |scenario|
29
+ !scenario.tags.include?('@mobile') &&
30
+ !scenario.tags.include?('@web')
31
+ end
32
+ mobile_tagged_count + empty_tagged_scenarios.count
33
+ end
34
+
35
+ def number_of_required_web_devices
36
+ all_tags = @scenarios.map(&:tags).flatten.uniq
37
+ all_tags.select { |tag| tag == '@web' }.count
38
+ end
39
+
40
+ def number_of_required_devices
41
+ all_tags = @scenarios.map(&:tags).flatten.uniq
42
+ all_tags.select { |tag| tag.start_with?('@user') }.count
43
+ end
44
+
45
+ def tags_for_user_id(user_id)
46
+ user_tag = "@user#{user_id}"
47
+ user_scenario = @scenarios.select do |scenario|
48
+ scenario.tags.include?(user_tag)
49
+ end.first
50
+ return [] if user_scenario.nil? || user_scenario.tags.nil?
51
+
52
+ user_scenario.tags.reject { |tag| tag == user_tag }
53
+ end
54
+
55
+ def right_syntax?
56
+ all_scenarios_have_a_user_tag? &&
57
+ only_one_user_tag_for_each_scenario? &&
58
+ !duplicate_tags_for_a_user?
59
+ end
60
+
61
+ def duplicate_tags_for_a_user?
62
+ taken_user_tags = {}
63
+ scenarios.each do |scenario|
64
+ user_tag = scenario.tags.select do |tag|
65
+ tag.start_with?('@user')
66
+ end.first
67
+ return true unless taken_user_tags[user_tag].nil?
68
+
69
+ taken_user_tags[user_tag] = user_tag
70
+ end
71
+ false
72
+ end
73
+
74
+ def only_one_user_tag_for_each_scenario?
75
+ scenarios.each do |scenario|
76
+ user_tags = scenario.tags.select do |tag|
77
+ tag.start_with?('@user')
78
+ end
79
+ return false if user_tags.count != 1
80
+ end
81
+ true
82
+ end
83
+
84
+ def all_scenarios_have_a_user_tag?
85
+ scenarios.each do |scenario|
86
+ user_tag = scenario.tags.select do |tag|
87
+ tag.start_with?('@user')
88
+ end.first
89
+ return false if user_tag.nil?
90
+ end
91
+ true
92
+ end
93
+
94
+ private
95
+
96
+ def read_content
97
+ parser = Gherkin::Parser.new
98
+ file_content = File.open(file_path).read
99
+ gherkin_document = parser.parse(file_content)
100
+ pickles = Gherkin::Pickles::Compiler.new.compile(gherkin_document)
101
+ pickles.each do |scenario|
102
+ scenarios << FeatureScenario.new(
103
+ name: scenario[:name],
104
+ tags: scenario[:tags]
105
+ )
106
+ end
107
+ end
108
+ 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,86 @@
1
+ require 'kraken-mobile/models/device'
2
+
3
+ class WebDevice < Device
4
+ #-------------------------------
5
+ # Signaling
6
+ #-------------------------------
7
+ def create_inbox
8
+ File.open(inbox_file_path, 'w')
9
+ end
10
+
11
+ def delete_inbox
12
+ return unless File.exist? inbox_file_path
13
+
14
+ File.delete(inbox_file_path)
15
+ end
16
+
17
+ def write_signal(signal)
18
+ File.open(inbox_file_path, 'a') do |file|
19
+ file.puts(signal)
20
+ end
21
+ end
22
+
23
+ def read_signal(signal, timeout = K::DEFAULT_TIMEOUT_SECONDS)
24
+ Timeout.timeout(timeout, RuntimeError) do
25
+ sleep(1) until inbox_last_signal == signal
26
+ end
27
+ end
28
+
29
+ #-------------------------------
30
+ # More interface methods
31
+ #-------------------------------
32
+ def connected?
33
+ true
34
+ end
35
+
36
+ def orientation
37
+ K::WEB_PORTRAIT
38
+ end
39
+
40
+ def screen_size
41
+ height = 0
42
+ width = 0
43
+
44
+ [height, width]
45
+ end
46
+
47
+ def sdk_version
48
+ 1.0 # Default
49
+ end
50
+
51
+ def type
52
+ K::WEB_DEVICE
53
+ end
54
+
55
+ #-------------------------------
56
+ # Random testing
57
+ #-------------------------------
58
+ def run_monkey_with_number_of_events(number_of_events)
59
+ number_of_events # TODO, implement
60
+ end
61
+
62
+ def run_kraken_monkey_with_number_of_events(number_of_events)
63
+ number_of_events # TODO, implement
64
+ end
65
+
66
+ #-------------------------------
67
+ # Helpers
68
+ #-------------------------------
69
+ def self.factory_create
70
+ WebDevice.new(
71
+ id: SecureRandom.hex(10),
72
+ model: 'Web'
73
+ )
74
+ end
75
+
76
+ private
77
+
78
+ def inbox_file_path
79
+ ".#{@id}_#{K::INBOX_FILE_NAME}"
80
+ end
81
+
82
+ def inbox_last_signal
83
+ lines = File.open(inbox_file_path).to_a
84
+ lines.last&.strip
85
+ end
86
+ 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
@@ -18,6 +18,21 @@ ParameterType(
18
18
  }
19
19
  )
20
20
 
21
+ ParameterType(
22
+ name: 'property',
23
+ regexp: /[^\"]*/,
24
+ type: String,
25
+ transformer: ->(s) {
26
+ channel = @scenario_tags.grep(/@user/).first
27
+ if ENV["PROPERTIES_PATH"] && channel && s.start_with?("<") && s.end_with?(">")
28
+ s.slice!('$')
29
+ return properties[channel][s]
30
+ else
31
+ return s
32
+ end
33
+ }
34
+ )
35
+
21
36
  Then /^I wait for a signal containing "([^\"]*)"$/ do |string|
22
37
  channel = @scenario_tags.grep(/@user/).first
23
38
  readSignal(channel, string, KrakenMobile::Constants::DEFAULT_TIMEOUT)
@@ -0,0 +1,82 @@
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
+ JSON.parse(content)
68
+ end
69
+
70
+ def handle_faker(key)
71
+ faker = KrakenFaker.new(process_id: current_process_id)
72
+ faker.generate_value_for_key(
73
+ key: key
74
+ )
75
+ end
76
+
77
+ def handle_faker_reuse(key)
78
+ faker = KrakenFaker.new(process_id: current_process_id)
79
+ faker.reuse_value_for_key(
80
+ key: key
81
+ )
82
+ end
@@ -0,0 +1,72 @@
1
+ require 'calabash-android/calabash_steps'
2
+ require 'kraken-mobile/utils/K'
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