kraken-mobile 1.0.1 → 1.0.8
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 -77
- 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 +91 -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/css/scenario_index.css +20 -0
- data/reporter/assets/images/krakenThumbnail.jpg +0 -0
- data/reporter/assets/js/sankey.js +56 -24
- data/reporter/feature_report.html.erb +5 -1
- data/reporter/index.html.erb +15 -8
- data/reporter/scenario_report.html.erb +73 -31
- metadata +95 -14
- 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,17 @@
|
|
1
|
+
require 'kraken-mobile/models/feature_file'
|
2
|
+
|
3
|
+
module Utils
|
4
|
+
module FeatureReader
|
5
|
+
def feature_files
|
6
|
+
features_dir = File.join(FileUtils.pwd, K::FEATURES_PATH)
|
7
|
+
unless File.exist?(features_dir)
|
8
|
+
raise "ERROR: File or directory '#{features_dir}' does not exists"
|
9
|
+
end
|
10
|
+
# Is a file not directory
|
11
|
+
return [features_dir] if features_dir.include?('.feature')
|
12
|
+
|
13
|
+
files = Dir[File.join(features_dir, '**{,/*/**}/*')].uniq
|
14
|
+
files.grep(/\.feature$/)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module K
|
4
|
+
SEPARATOR = ';' unless defined? SEPARATOR
|
5
|
+
DIRECTORY_PATH = '.device_directory' unless defined? DIRECTORY_PATH
|
6
|
+
DICTIONARY_PATH = 'dictionary.json' unless defined? DICTIONARY_PATH
|
7
|
+
INBOX_FILE_NAME = 'inbox.txt' unless defined? INBOX_FILE_NAME
|
8
|
+
FEATURES_PATH = './features' unless defined? FEATURES_PATH
|
9
|
+
DEFAULT_TIMEOUT_SECONDS = 30 unless defined? DEFAULT_TIMEOUT_SECONDS
|
10
|
+
ANDROID_PORTRAIT = 0 unless defined? ANDROID_PORTRAIT
|
11
|
+
ANDROID_LANDSCAPE = 1 unless defined? ANDROID_LANDSCAPE
|
12
|
+
WEB_PORTRAIT = 1 unless defined? WEB_PORTRAIT
|
13
|
+
MONKEY_DEFAULT_TIMEOUT = 5 unless defined? MONKEY_DEFAULT_TIMEOUT
|
14
|
+
WEB_DEVICE = 'WEB_DEVICE' unless defined? WEB_DEVICE
|
15
|
+
ANDROID_DEVICE = 'ANDROID_DEVICE' unless defined? ANDROID_DEVICE
|
16
|
+
PROPERTIES_PATH = 'PROPERTIES_PATH' unless defined? PROPERTIES_PATH
|
17
|
+
CONFIG_PATH = 'CONFIG_PATH' unless defined? CONFIG_PATH
|
18
|
+
REPORT_PATH = './reports' unless defined? REPORT_PATH
|
19
|
+
FILE_REPORT_NAME = 'report.json' unless defined? FILE_REPORT_NAME
|
20
|
+
D3_DATA_FILE_NAME = 'data.json' unless defined? D3_DATA_FILE_NAME
|
21
|
+
SCREENSHOT_PATH = 'SCREENSHOT_PATH' unless defined? SCREENSHOT_PATH
|
22
|
+
|
23
|
+
unless defined? DEVICES_REPORT_FILE_NAME
|
24
|
+
DEVICES_REPORT_FILE_NAME = 'devices.json'
|
25
|
+
end
|
26
|
+
|
27
|
+
unless defined? REPORT_ASSETS_PATH
|
28
|
+
REPORT_ASSETS_PATH = '../../../../reporter/assets/'
|
29
|
+
end
|
30
|
+
|
31
|
+
unless defined? DEFAULT_START_TIMEOUT_SECONDS
|
32
|
+
DEFAULT_START_TIMEOUT_SECONDS = 600 # 10.minutes
|
33
|
+
end
|
34
|
+
|
35
|
+
unless defined? DEFAULT_FINISH_TIMEOUT_SECONDS
|
36
|
+
DEFAULT_FINISH_TIMEOUT_SECONDS = 600 # 10.minutes
|
37
|
+
end
|
38
|
+
|
39
|
+
unless defined? DEVICES_READY_PATH
|
40
|
+
DEVICES_READY_PATH = '.devices_ready_to_start'
|
41
|
+
end
|
42
|
+
|
43
|
+
unless defined? DEVICES_FINISHED_PATH
|
44
|
+
DEVICES_FINISHED_PATH = '.devices_ready_to_finish'
|
45
|
+
end
|
46
|
+
|
47
|
+
unless defined? PROCESS_STATES
|
48
|
+
PROCESS_STATES = {
|
49
|
+
ready_to_start: 0,
|
50
|
+
ready_to_finish: 1
|
51
|
+
}.freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
unless defined? PROCESS_STATE_FILE_PATH
|
55
|
+
PROCESS_STATE_FILE_PATH = {
|
56
|
+
K::PROCESS_STATES[:ready_to_start] => DEVICES_READY_PATH,
|
57
|
+
K::PROCESS_STATES[:ready_to_finish] => DEVICES_FINISHED_PATH
|
58
|
+
}.freeze
|
59
|
+
end
|
60
|
+
|
61
|
+
unless defined? CALABASH_MONKEY_ACTIONS
|
62
|
+
CALABASH_MONKEY_ACTIONS = [
|
63
|
+
:move,
|
64
|
+
:down,
|
65
|
+
:up
|
66
|
+
].freeze
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,500 @@
|
|
1
|
+
require 'kraken-mobile/utils/k.rb'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class Reporter
|
5
|
+
#-------------------------------
|
6
|
+
# Fields
|
7
|
+
#-------------------------------
|
8
|
+
PASSED = 'passed'.freeze
|
9
|
+
FAILED = 'failed'.freeze
|
10
|
+
SKIPPED = 'skipped'.freeze
|
11
|
+
PENDING = 'pending'.freeze
|
12
|
+
NOT_DEFINED = 'undefined'.freeze
|
13
|
+
AMBIGUOUS = 'ambiguous'.freeze
|
14
|
+
|
15
|
+
attr_accessor :test_scenario
|
16
|
+
|
17
|
+
#-------------------------------
|
18
|
+
# Constructors
|
19
|
+
#-------------------------------
|
20
|
+
def initialize(test_scenario:)
|
21
|
+
@test_scenario = test_scenario
|
22
|
+
end
|
23
|
+
|
24
|
+
#-------------------------------
|
25
|
+
# Lifecycle
|
26
|
+
#-------------------------------
|
27
|
+
def create_report_folder_requirements
|
28
|
+
create_report_execution_report_folder
|
29
|
+
create_devices_execution_report_folder
|
30
|
+
save_execution_devices_list
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_report
|
34
|
+
generate_each_device_report
|
35
|
+
generate_general_report
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate_general_report
|
39
|
+
erb_file = File.join(
|
40
|
+
File.expand_path('../../../../reporter', __FILE__),
|
41
|
+
'index.html.erb'
|
42
|
+
)
|
43
|
+
html_file = File.join(
|
44
|
+
File.expand_path("#{K::REPORT_PATH}/#{test_execution_id}/"),
|
45
|
+
'index.html'
|
46
|
+
)
|
47
|
+
report_file = open(
|
48
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/#{K::DEVICES_REPORT_FILE_NAME}"
|
49
|
+
)
|
50
|
+
content = report_file.read
|
51
|
+
report_file.close
|
52
|
+
devices_report = report_by_devices
|
53
|
+
@features_report = fetures_from_report_by_devices(devices_report)
|
54
|
+
data_hash = feature_by_nodes_and_links @features_report
|
55
|
+
open(
|
56
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/assets/js/#{K::D3_DATA_FILE_NAME}",
|
57
|
+
'w'
|
58
|
+
) do |file|
|
59
|
+
file.puts(data_hash.to_json)
|
60
|
+
end
|
61
|
+
template = File.read(erb_file)
|
62
|
+
result = ERB.new(template).result(binding)
|
63
|
+
File.open(html_file, 'w+') do |f|
|
64
|
+
f.write result
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_each_device_report
|
69
|
+
devices.each_with_index do |device, index|
|
70
|
+
generate_device_report(device, index + 1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate_device_report(device, id)
|
75
|
+
if device.is_a? AndroidDevice
|
76
|
+
generate_mobile_report(device, id)
|
77
|
+
elsif device.is_a? WebDevice
|
78
|
+
generate_web_report(device, id)
|
79
|
+
else
|
80
|
+
raise 'ERROR: Platform not supported'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_mobile_report(device, id)
|
85
|
+
process = MobileProcess.new(
|
86
|
+
id: id,
|
87
|
+
device: device,
|
88
|
+
test_scenario: @test_scenario
|
89
|
+
)
|
90
|
+
@apk_path = process.apk_path
|
91
|
+
report_file = open("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}")
|
92
|
+
content = report_file.read
|
93
|
+
report_file.close
|
94
|
+
@features = JSON.parse(content)
|
95
|
+
@total_scenarios = total_scenarios @features
|
96
|
+
@device = device
|
97
|
+
@total_failed_scenarios_percentage = total_failed_scenarios_percentage @features
|
98
|
+
@total_passed_scenarios_percentage = total_passed_scenarios_percentage @features
|
99
|
+
@total_passed_features_percentage = total_passed_features_percentage @features
|
100
|
+
@total_failed_features_percentage = total_failed_features_percentage @features
|
101
|
+
erb_file = File.join(File.expand_path('../../../../reporter', __FILE__), "feature_report.html.erb")
|
102
|
+
html_file = File.join(File.expand_path("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/"), File.basename(erb_file, '.erb')) #=>"page.html"
|
103
|
+
# Variables
|
104
|
+
template = File.read(erb_file)
|
105
|
+
result = ERB.new(template).result(binding)
|
106
|
+
# write result to file
|
107
|
+
File.open(html_file, 'w+') do |f|
|
108
|
+
f.write result
|
109
|
+
end
|
110
|
+
generate_features_report @features, device
|
111
|
+
end
|
112
|
+
|
113
|
+
def generate_web_report(device, id)
|
114
|
+
process = WebProcess.new(
|
115
|
+
id: id,
|
116
|
+
device: device,
|
117
|
+
test_scenario: @test_scenario
|
118
|
+
)
|
119
|
+
@apk_path = nil
|
120
|
+
report_file = open("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}")
|
121
|
+
content = report_file.read
|
122
|
+
report_file.close
|
123
|
+
@features = JSON.parse(content)
|
124
|
+
@total_scenarios = total_scenarios @features
|
125
|
+
@device = device
|
126
|
+
@total_failed_scenarios_percentage = total_failed_scenarios_percentage @features
|
127
|
+
@total_passed_scenarios_percentage = total_passed_scenarios_percentage @features
|
128
|
+
@total_passed_features_percentage = total_passed_features_percentage @features
|
129
|
+
@total_failed_features_percentage = total_failed_features_percentage @features
|
130
|
+
erb_file = File.join(File.expand_path('../../../../reporter', __FILE__), "feature_report.html.erb")
|
131
|
+
html_file = File.join(File.expand_path("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/"), File.basename(erb_file, '.erb')) #=>"page.html"
|
132
|
+
# Variables
|
133
|
+
template = File.read(erb_file)
|
134
|
+
result = ERB.new(template).result(binding)
|
135
|
+
# write result to file
|
136
|
+
File.open(html_file, 'w+') do |f|
|
137
|
+
f.write result
|
138
|
+
end
|
139
|
+
generate_features_report @features, device
|
140
|
+
end
|
141
|
+
|
142
|
+
#-------------------------------
|
143
|
+
# Methods
|
144
|
+
#-------------------------------
|
145
|
+
def test_execution_id
|
146
|
+
raise 'ERROR: Invalid test scenario' if @test_scenario.nil?
|
147
|
+
|
148
|
+
@test_scenario.execution_id
|
149
|
+
end
|
150
|
+
|
151
|
+
def create_report_execution_report_folder
|
152
|
+
Dir.mkdir(K::REPORT_PATH) unless File.exist?(K::REPORT_PATH)
|
153
|
+
Dir.mkdir("#{K::REPORT_PATH}/#{test_execution_id}")
|
154
|
+
Dir.mkdir(screenshot_path)
|
155
|
+
FileUtils.cp_r(
|
156
|
+
File.expand_path(K::REPORT_ASSETS_PATH, __FILE__),
|
157
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/"
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
def save_execution_devices_list
|
162
|
+
open(
|
163
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/#{K::DEVICES_REPORT_FILE_NAME}",
|
164
|
+
'w'
|
165
|
+
) do |file|
|
166
|
+
file.puts(devices_json.to_json)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def create_devices_execution_report_folder
|
171
|
+
devices.each do |device|
|
172
|
+
Dir.mkdir(
|
173
|
+
"#{KrakenMobile::Constants::REPORT_PATH}/#{test_execution_id}/"\
|
174
|
+
"#{device.id}"
|
175
|
+
)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def screenshot_path
|
180
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/screenshots/"
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def generate_features_report features, device
|
186
|
+
features.each do |feature|
|
187
|
+
generate_feature_report feature, device
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def generate_feature_report feature, device
|
192
|
+
Dir.mkdir("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/features_report") unless File.exists?("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/features_report")
|
193
|
+
file_name = feature_id feature
|
194
|
+
erb_file = File.join(File.expand_path('../../../../reporter', __FILE__), "scenario_report.html.erb")
|
195
|
+
html_file = File.join(File.expand_path("#{K::REPORT_PATH}/#{test_execution_id}/#{device.id}/features_report"), "#{file_name}.html") #=>"page.html"
|
196
|
+
# Variables
|
197
|
+
@feature = feature
|
198
|
+
template = File.read(erb_file)
|
199
|
+
result = ERB.new(template).result(binding)
|
200
|
+
# write result to file
|
201
|
+
File.open(html_file, 'w+') do |f|
|
202
|
+
f.write result
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def report_by_devices
|
207
|
+
devices_report = {}
|
208
|
+
devices_json.each do |device|
|
209
|
+
unless File.exist?(
|
210
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/#{device[:id]}/#{K::FILE_REPORT_NAME}"
|
211
|
+
)
|
212
|
+
next
|
213
|
+
end
|
214
|
+
|
215
|
+
report_file = open(
|
216
|
+
"#{K::REPORT_PATH}/#{test_execution_id}/#{device[:id]}/#{K::FILE_REPORT_NAME}"
|
217
|
+
)
|
218
|
+
content = report_file.read
|
219
|
+
report_file.close
|
220
|
+
devices_report[device[:user]] = JSON.parse(content)
|
221
|
+
devices_report[device[:user]].each do |d| d['device_model'] = device[:model] if !d['device_model'] end
|
222
|
+
devices_report[device[:user]].each do |d| d['device_id'] = device[:id] if !d['device_id'] end
|
223
|
+
end
|
224
|
+
devices_report
|
225
|
+
end
|
226
|
+
|
227
|
+
def fetures_from_report_by_devices report_by_devices
|
228
|
+
features = {}
|
229
|
+
report_by_devices.keys.each do |user_key|
|
230
|
+
report = report_by_devices[user_key]
|
231
|
+
report.each do |feature|
|
232
|
+
features[feature["id"]] = {} if !features[feature["id"]]
|
233
|
+
features[feature["id"]]["name"] = feature["name"] if !features[feature["id"]]["name"] && feature["name"]
|
234
|
+
features[feature["id"]]["devices"] = {} if !features[feature["id"]]["devices"]
|
235
|
+
if feature["elements"] && feature["elements"].count > 0
|
236
|
+
features[feature["id"]]["devices"][user_key] = []
|
237
|
+
if feature["elements"].first["steps"]
|
238
|
+
failed = false
|
239
|
+
feature["elements"].first["steps"].each do |step|
|
240
|
+
next if failed
|
241
|
+
failed = step["result"]["status"] != PASSED
|
242
|
+
image = nil
|
243
|
+
image = step["after"].first["embeddings"].first["data"] if step["after"] && step["after"].count > 0 && step["after"].first["embeddings"] && step["after"].first["embeddings"].count > 0
|
244
|
+
features[feature["id"]]["devices"][user_key] << {
|
245
|
+
name: "#{step['keyword']} #{step['name']}",
|
246
|
+
duration: step["result"]["duration"],
|
247
|
+
image: image,
|
248
|
+
device_model: feature["device_model"],
|
249
|
+
status: failed ? FAILED : PASSED
|
250
|
+
}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
features
|
257
|
+
end
|
258
|
+
|
259
|
+
def feature_by_nodes_and_links features_report
|
260
|
+
features = []
|
261
|
+
features_report.values.each do |feature|
|
262
|
+
features << nodes_and_links(feature["devices"], feature["name"]) if feature["devices"]
|
263
|
+
end
|
264
|
+
features
|
265
|
+
end
|
266
|
+
|
267
|
+
def nodes_and_links feature_report, feature_name
|
268
|
+
last_node_id = 0
|
269
|
+
nodes = [{ name: "", id: "empty", image: nil }]
|
270
|
+
signal_hash = {}
|
271
|
+
links = []
|
272
|
+
feature_report.keys.each do |key|
|
273
|
+
steps = feature_report[key]
|
274
|
+
coming_from_signal = false
|
275
|
+
last_signal = -1
|
276
|
+
steps.each_with_index do |step, index|
|
277
|
+
node_id = last_node_id+1
|
278
|
+
if isReadSignal(step[:name]) && step[:status] == PASSED
|
279
|
+
signal = signalContent(step[:name])
|
280
|
+
already_created_signal = signal_hash[signal] ? true : false
|
281
|
+
signal_hash[signal] = already_created_signal ? signal_hash[signal] : { id: "#{node_id}", receiver: key }
|
282
|
+
node = { name: "Signal: #{signal}, Receiver: #{step[:device_model]}", id: signal_hash[signal][:id], image: nil, status: step[:status] }
|
283
|
+
if already_created_signal
|
284
|
+
entry = nodes.select{ |node| node[:id] == signal_hash[signal][:id] }.first
|
285
|
+
entry[:name] = "Signal: #{signal}, Receiver: #{step[:device_model]}" if entry
|
286
|
+
end
|
287
|
+
source = (coming_from_signal ? last_signal : (index == 0 ? 0 : last_node_id))
|
288
|
+
link = {
|
289
|
+
source: source,
|
290
|
+
target: signal_hash[signal][:id].to_i,
|
291
|
+
value: 1,
|
292
|
+
owner: key,
|
293
|
+
owner_model: step[:device_model]
|
294
|
+
}
|
295
|
+
nodes << node if !already_created_signal
|
296
|
+
links << link
|
297
|
+
last_node_id += 1 if !already_created_signal
|
298
|
+
last_signal = signal_hash[signal][:id].to_i
|
299
|
+
coming_from_signal = true
|
300
|
+
elsif isWriteSignal(step[:name]) && step[:status] == PASSED
|
301
|
+
signal = signalContent(step[:name])
|
302
|
+
receiver = signalReceiver(step[:name])
|
303
|
+
already_created_signal = signal_hash[signal] ? true : false
|
304
|
+
signal_hash[signal] = already_created_signal ? signal_hash[signal] : { id: "#{node_id}", receiver: receiver }
|
305
|
+
node = { name: step[:name], id: signal_hash[signal][:id], image: nil, status: step[:status] }
|
306
|
+
source = (coming_from_signal ? last_signal : (index == 0 ? 0 : last_node_id))
|
307
|
+
link = {
|
308
|
+
source: source,
|
309
|
+
target: signal_hash[signal][:id].to_i,
|
310
|
+
value: 1,
|
311
|
+
owner: key,
|
312
|
+
owner_model: step[:device_model]
|
313
|
+
}
|
314
|
+
nodes << node if !already_created_signal
|
315
|
+
links << link
|
316
|
+
last_node_id += 1 if !already_created_signal
|
317
|
+
last_signal = signal_hash[signal][:id].to_i
|
318
|
+
coming_from_signal = true
|
319
|
+
else
|
320
|
+
node = { name: step[:name], id: "#{node_id}", image: step[:image], status: step[:status] }
|
321
|
+
source = (coming_from_signal ? last_signal : (index == 0 ? 0 : last_node_id))
|
322
|
+
link = {
|
323
|
+
source: source,
|
324
|
+
target: node_id,
|
325
|
+
value: 1,
|
326
|
+
owner: key,
|
327
|
+
owner_model: step[:device_model]
|
328
|
+
}
|
329
|
+
nodes << node
|
330
|
+
links << link
|
331
|
+
last_node_id += 1
|
332
|
+
coming_from_signal = false
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
return {
|
337
|
+
name: feature_name,
|
338
|
+
nodes: nodes,
|
339
|
+
links: links
|
340
|
+
}
|
341
|
+
end
|
342
|
+
|
343
|
+
def isReadSignal step
|
344
|
+
line = step.split(' ')[1..-1].join(' ')
|
345
|
+
(line =~ /^I wait for a signal containing "([^\"]*)"$/ ? true : false) || (line =~ /^I wait for a signal containing "([^\"]*)" for (\d+) seconds$/ ? true : false)
|
346
|
+
end
|
347
|
+
|
348
|
+
def isWriteSignal step
|
349
|
+
line = step.split(' ')[1..-1].join(' ')
|
350
|
+
line =~ /^I send a signal to user (\d+) containing "([^\"]*)"$/ ? true : false
|
351
|
+
end
|
352
|
+
|
353
|
+
def signalContent step
|
354
|
+
line = step.split(' ')[1..-1].join(' ')
|
355
|
+
line.scan(/"([^\"]*)"/).first.first if line.scan(/"([^\"]*)"/).first
|
356
|
+
end
|
357
|
+
|
358
|
+
def signalReceiver step
|
359
|
+
line = step.split(' ')[1..-1].join(' ')
|
360
|
+
line.scan(/(\d+)/).first.first if line.scan(/(\d+)/).first
|
361
|
+
end
|
362
|
+
|
363
|
+
def devices
|
364
|
+
raise 'ERROR: Invalid test scenario' if @test_scenario.nil?
|
365
|
+
|
366
|
+
@test_scenario.devices.compact
|
367
|
+
end
|
368
|
+
|
369
|
+
def devices_json
|
370
|
+
devices.map.with_index do |device, index|
|
371
|
+
height, width = device.screen_size
|
372
|
+
{
|
373
|
+
user: (index + 1), id: device.id,
|
374
|
+
model: device.model, screen_height: height,
|
375
|
+
screen_width: width, sdk: device.sdk_version,
|
376
|
+
type: device.type
|
377
|
+
}
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def total_scenarios features
|
382
|
+
how_many = 0
|
383
|
+
features.each do |feature|
|
384
|
+
scenarios = feature["elements"]
|
385
|
+
how_many += scenarios.count if scenarios
|
386
|
+
end
|
387
|
+
how_many
|
388
|
+
end
|
389
|
+
|
390
|
+
def failed_scenarios feature
|
391
|
+
scenarios = feature["elements"]
|
392
|
+
scenarios.select{ |scenario|
|
393
|
+
steps = scenario["steps"]
|
394
|
+
steps.any?{ |step| step["result"] && step["result"]["status"] != PASSED }
|
395
|
+
}
|
396
|
+
end
|
397
|
+
|
398
|
+
def total_passed_scenarios features
|
399
|
+
how_many = 0
|
400
|
+
features.each do |feature|
|
401
|
+
how_many += passed_scenarios(feature).count
|
402
|
+
end
|
403
|
+
how_many
|
404
|
+
end
|
405
|
+
|
406
|
+
def total_failed_scenarios features
|
407
|
+
how_many = 0
|
408
|
+
features.each do |feature|
|
409
|
+
how_many += failed_scenarios(feature).count
|
410
|
+
end
|
411
|
+
how_many
|
412
|
+
end
|
413
|
+
|
414
|
+
def total_passed_features features
|
415
|
+
how_many = 0
|
416
|
+
features.each do |feature|
|
417
|
+
how_many += 1 if feature_passed?(feature)
|
418
|
+
end
|
419
|
+
how_many
|
420
|
+
end
|
421
|
+
|
422
|
+
def total_failed_features features
|
423
|
+
how_many = 0
|
424
|
+
features.each do |feature|
|
425
|
+
how_many += 1 if !feature_passed?(feature)
|
426
|
+
end
|
427
|
+
how_many
|
428
|
+
end
|
429
|
+
|
430
|
+
def feature_passed_scenarios_percentage feature
|
431
|
+
(passed_scenarios(feature).count.to_f/feature["elements"].count.to_f).round(2) * 100.00
|
432
|
+
end
|
433
|
+
|
434
|
+
def feature_failed_scenarios_percentage feature
|
435
|
+
(failed_scenarios(feature).count.to_f/feature["elements"].count.to_f).round(2) * 100.00
|
436
|
+
end
|
437
|
+
|
438
|
+
def total_passed_scenarios_percentage features
|
439
|
+
(total_passed_scenarios(features).to_f/total_scenarios(features).to_f).round(2) * 100.00
|
440
|
+
end
|
441
|
+
|
442
|
+
def total_passed_features_percentage features
|
443
|
+
(total_passed_features(features).to_f/features.count.to_f).round(2) * 100.00
|
444
|
+
end
|
445
|
+
|
446
|
+
def total_failed_scenarios_percentage features
|
447
|
+
(total_failed_scenarios(features).to_f/total_scenarios(features).to_f).round(2) * 100.00
|
448
|
+
end
|
449
|
+
|
450
|
+
def total_failed_features_percentage features
|
451
|
+
(total_failed_features(features).to_f/features.count.to_f).round(2) * 100.00
|
452
|
+
end
|
453
|
+
|
454
|
+
def feature_passed? feature
|
455
|
+
passed_scenarios(feature).count == feature["elements"].count
|
456
|
+
end
|
457
|
+
|
458
|
+
def format_duration(nanoseconds)
|
459
|
+
duration_in_seconds = nanoseconds.to_f/1000000000.0
|
460
|
+
m, s = duration_in_seconds.divmod(60)
|
461
|
+
"#{m}m #{format('%.3f', s)}s"
|
462
|
+
end
|
463
|
+
|
464
|
+
def passed_features features
|
465
|
+
features.select{ |feature| passed_scenarios(feature) == feature["elements"].count }
|
466
|
+
end
|
467
|
+
|
468
|
+
def failed_features features
|
469
|
+
features.select{ |feature| failed_scenarios(feature) == feature["elements"].count }
|
470
|
+
end
|
471
|
+
|
472
|
+
def feature_duration feature
|
473
|
+
scenarios = feature["elements"]
|
474
|
+
how_long = 0
|
475
|
+
scenarios.each do |scenario|
|
476
|
+
how_long += scenario_duration(scenario)
|
477
|
+
end
|
478
|
+
how_long
|
479
|
+
end
|
480
|
+
|
481
|
+
def scenario_duration scenario
|
482
|
+
how_long = 0
|
483
|
+
scenario["steps"].each do |step|
|
484
|
+
how_long += step["result"]["duration"] if step["result"] && step["result"]["duration"]
|
485
|
+
end
|
486
|
+
how_long
|
487
|
+
end
|
488
|
+
|
489
|
+
def passed_scenarios feature
|
490
|
+
scenarios = feature["elements"]
|
491
|
+
scenarios.select{ |scenario|
|
492
|
+
steps = scenario["steps"]
|
493
|
+
steps.all?{ |step| step["result"] && step["result"]["status"] == PASSED }
|
494
|
+
}
|
495
|
+
end
|
496
|
+
|
497
|
+
def feature_id feature
|
498
|
+
Digest::SHA256.hexdigest("#{feature["id"].strip}#{feature["uri"].strip}")
|
499
|
+
end
|
500
|
+
end
|