kraken-mobile 1.0.4 → 1.0.5

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