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,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
|