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,41 @@
|
|
1
|
+
require 'kraken-mobile/device_process.rb'
|
2
|
+
|
3
|
+
class WebProcess < DeviceProcess
|
4
|
+
#-------------------------------
|
5
|
+
# Required methods
|
6
|
+
#-------------------------------
|
7
|
+
def before_execute
|
8
|
+
register_process_to_directory
|
9
|
+
device.create_inbox
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_execute
|
13
|
+
unregister_process_from_directory
|
14
|
+
device.delete_inbox
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
open(execution_command, 'r') do |output|
|
19
|
+
loop do
|
20
|
+
$stdout.print output.readline.to_s
|
21
|
+
$stdout.flush
|
22
|
+
end
|
23
|
+
end
|
24
|
+
$CHILD_STATUS.exitstatus
|
25
|
+
rescue EOFError
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def execution_command
|
32
|
+
feature_path = test_scenario.feature_file.file_path
|
33
|
+
raise 'ERROR: Invalid feature file path' if feature_path.nil?
|
34
|
+
|
35
|
+
"|cucumber #{feature_path} --tags @user#{id} \
|
36
|
+
--require features/web/step_definitions/web_steps.rb \
|
37
|
+
--require features/web/support/app_life_cycle_hooks.rb \
|
38
|
+
--format pretty --format json -o \
|
39
|
+
#{K::REPORT_PATH}/#{@test_scenario.execution_id}/#{device.id}/#{K::FILE_REPORT_NAME}"
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'kraken-mobile/utils/feature_reader'
|
2
|
+
require 'kraken-mobile/test_scenario'
|
3
|
+
|
4
|
+
class KrakenApp
|
5
|
+
include Utils::FeatureReader
|
6
|
+
|
7
|
+
#-------------------------------
|
8
|
+
# Fields
|
9
|
+
#-------------------------------
|
10
|
+
|
11
|
+
attr_accessor :apk_path
|
12
|
+
attr_accessor :scenarios_queue
|
13
|
+
|
14
|
+
#-------------------------------
|
15
|
+
# Constructors
|
16
|
+
#-------------------------------
|
17
|
+
def initialize(apk_path:, properties_path: nil, config_path: nil)
|
18
|
+
@apk_path = apk_path
|
19
|
+
@scenarios_queue = []
|
20
|
+
save_path_in_environment_variable_with_name(
|
21
|
+
name: K::PROPERTIES_PATH, path: properties_path
|
22
|
+
)
|
23
|
+
save_path_in_environment_variable_with_name(
|
24
|
+
name: K::CONFIG_PATH, path: config_path
|
25
|
+
)
|
26
|
+
|
27
|
+
build_scenarios_queue
|
28
|
+
end
|
29
|
+
|
30
|
+
#-------------------------------
|
31
|
+
# Observers
|
32
|
+
#-------------------------------
|
33
|
+
def on_test_scenario_finished
|
34
|
+
execute_next_scenario
|
35
|
+
end
|
36
|
+
|
37
|
+
#-------------------------------
|
38
|
+
# Helpers
|
39
|
+
#-------------------------------
|
40
|
+
|
41
|
+
def start
|
42
|
+
execute_next_scenario
|
43
|
+
end
|
44
|
+
|
45
|
+
def save_path_in_environment_variable_with_name(name:, path:)
|
46
|
+
return if path.nil?
|
47
|
+
|
48
|
+
absolute_path = File.expand_path(path)
|
49
|
+
save_value_in_environment_variable_with_name(
|
50
|
+
name: name,
|
51
|
+
value: absolute_path
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def save_value_in_environment_variable_with_name(name:, value:)
|
56
|
+
return if name.nil? || value.nil?
|
57
|
+
|
58
|
+
ENV[name] = value
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def build_scenarios_queue
|
64
|
+
feature_files.each do |feature_path|
|
65
|
+
scenarios_queue.unshift(
|
66
|
+
TestScenario.new(
|
67
|
+
kraken_app: self,
|
68
|
+
feature_file_path: feature_path
|
69
|
+
)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def execute_next_scenario
|
75
|
+
return if scenarios_queue.count.zero?
|
76
|
+
|
77
|
+
scenario = scenarios_queue.pop
|
78
|
+
scenario.run
|
79
|
+
scenario
|
80
|
+
end
|
81
|
+
end
|
@@ -459,3 +459,23 @@ background-color: #E74C3C;
|
|
459
459
|
padding: 50px 0;
|
460
460
|
text-align: center;
|
461
461
|
}
|
462
|
+
|
463
|
+
.step_title {
|
464
|
+
margin-bottom: 10px;
|
465
|
+
white-space:nowrap;
|
466
|
+
overflow-x: hidden;
|
467
|
+
text-overflow: ellipsis;
|
468
|
+
}
|
469
|
+
|
470
|
+
.step_title_icon {
|
471
|
+
margin-right: 5px;
|
472
|
+
}
|
473
|
+
|
474
|
+
.not_available {
|
475
|
+
text-align: center;
|
476
|
+
width: 100%;
|
477
|
+
}
|
478
|
+
|
479
|
+
.step_image {
|
480
|
+
cursor: pointer;
|
481
|
+
}
|
Binary file
|
@@ -110,11 +110,21 @@
|
|
110
110
|
return 10;
|
111
111
|
})
|
112
112
|
.style("fill", e)
|
113
|
+
.style("cursor", function(t) {
|
114
|
+
if(t.image) {
|
115
|
+
return "pointer";
|
116
|
+
} else {
|
117
|
+
return "default";
|
118
|
+
}
|
119
|
+
})
|
113
120
|
.on("mouseover", function(t) {
|
114
121
|
mouseover(t, i);
|
115
122
|
}).on("mouseout", function(t) {
|
116
123
|
mouseout(t, i);
|
117
124
|
})
|
125
|
+
.on("click", function(t) {
|
126
|
+
click(t, i);
|
127
|
+
})
|
118
128
|
},
|
119
129
|
exit: function() {
|
120
130
|
this.remove()
|
@@ -442,7 +452,7 @@ function mouseover(d, contexter) {
|
|
442
452
|
computedHeight = boundingRect.height;
|
443
453
|
|
444
454
|
let xPosition = d.x + margins.left + d.dx + 10;
|
445
|
-
let yPosition =
|
455
|
+
let yPosition = d.y + d.dy/2;
|
446
456
|
let endXPosition = xPosition + computedWidth;
|
447
457
|
|
448
458
|
if(endXPosition > width) {
|
@@ -457,38 +467,50 @@ function mouseover(d, contexter) {
|
|
457
467
|
im.id = "scene" + d.id;
|
458
468
|
im.src = "data:image/png;base64," + d.image
|
459
469
|
im.onload = function(e) {
|
460
|
-
var
|
461
|
-
var h = this.height;
|
470
|
+
var h = 100;
|
462
471
|
var ixPosition = d.x + margins.left + d.dx + 10; // 10 of separation from node
|
463
|
-
var iyPosition =
|
464
|
-
let endiXPosition = ixPosition + w;
|
465
|
-
let endiYPosition = iyPosition + h;
|
466
|
-
|
467
|
-
var i = 1
|
468
|
-
while(w/i > 100 || h/i > 100) {
|
469
|
-
i++;
|
470
|
-
}
|
471
|
-
|
472
|
-
h = h/i;
|
473
|
-
w = w/i;
|
474
|
-
|
475
|
-
endiXPosition = ixPosition + w;
|
476
|
-
endiYPosition = iyPosition + h;
|
472
|
+
var iyPosition = d.y + d.dy/2 + 10; // 10 of separation from label
|
477
473
|
|
478
|
-
|
479
|
-
ixPosition -= (endiXPosition-width)
|
480
|
-
}
|
481
|
-
|
482
|
-
contexter.base.append("image")
|
474
|
+
image = contexter.base.append("image")
|
483
475
|
.data([this])
|
484
476
|
.attr("x", ixPosition)
|
485
477
|
.attr("y", iyPosition)
|
478
|
+
.attr("height", 100)
|
479
|
+
.attr("width", 50)
|
480
|
+
.style("width", "auto")
|
486
481
|
.attr("xlink:href", this.src)
|
487
482
|
.attr("transform", null)
|
488
483
|
.attr("id", this.id)
|
489
484
|
.attr("class", "scene-image")
|
490
|
-
|
491
|
-
.
|
485
|
+
|
486
|
+
imageBoundingRect = image.node().getBoundingClientRect();
|
487
|
+
computedImageWidth = imageBoundingRect.width;
|
488
|
+
computedImageHeight = imageBoundingRect.height;
|
489
|
+
let endImageXPosition = ixPosition + computedImageWidth;
|
490
|
+
let endImageYPosition = iyPosition + computedImageHeight;
|
491
|
+
|
492
|
+
if(endImageXPosition > width) {
|
493
|
+
ixPosition -= (endImageXPosition-width)
|
494
|
+
}
|
495
|
+
|
496
|
+
if(endImageYPosition > height + margins.bottom) {
|
497
|
+
iyPosition -= (endImageYPosition-(height+margins.bottom))
|
498
|
+
}
|
499
|
+
|
500
|
+
image.attr("x", ixPosition)
|
501
|
+
.attr("y", iyPosition);
|
502
|
+
|
503
|
+
text.attr("y", iyPosition - 10);
|
504
|
+
|
505
|
+
contexter.base.append('rect')
|
506
|
+
.attr('class', 'scene-image')
|
507
|
+
.attr('width', computedImageWidth)
|
508
|
+
.attr('height', computedImageHeight)
|
509
|
+
.attr('fill', 'transparent')
|
510
|
+
.attr('stroke', 'black')
|
511
|
+
.style('opacity', 0.25)
|
512
|
+
.attr("x", ixPosition)
|
513
|
+
.attr("y", iyPosition);
|
492
514
|
}
|
493
515
|
}
|
494
516
|
}
|
@@ -497,6 +519,16 @@ function mouseout(d, svg) {
|
|
497
519
|
d3.selectAll("[class=\"scene-image\"]").remove();
|
498
520
|
}
|
499
521
|
|
522
|
+
function click(d, svg) {
|
523
|
+
if(d.image) {
|
524
|
+
var im = new Image();
|
525
|
+
im.src = "data:image/png;base64," + d.image
|
526
|
+
var w = window.open("",'_blank');
|
527
|
+
w.document.write(im.outerHTML);
|
528
|
+
w.document.close();
|
529
|
+
}
|
530
|
+
}
|
531
|
+
|
500
532
|
function label(node) {
|
501
533
|
return node.name.replace(/\s*\(.*?\)$/, '');
|
502
534
|
}
|
@@ -175,7 +175,11 @@
|
|
175
175
|
<%= device.model %> - <%= device.id %>
|
176
176
|
</td>
|
177
177
|
<td>
|
178
|
-
|
178
|
+
<% if device.type == K::ANDROID_DEVICE %>
|
179
|
+
<i class="fa fa-android fa-lg"><span>android</span></i>
|
180
|
+
<% else %>
|
181
|
+
<i class="fa fa-globe fa-lg"><span>web</span></i>
|
182
|
+
<% end %>
|
179
183
|
</td>
|
180
184
|
<td>
|
181
185
|
<%= @apk_path %>
|
data/reporter/index.html.erb
CHANGED
@@ -595,18 +595,24 @@
|
|
595
595
|
<div class="clearfix"></div>
|
596
596
|
</div>
|
597
597
|
<div class="row">
|
598
|
-
<%
|
598
|
+
<% devices_json.each do |device| %>
|
599
599
|
<div class="ui-card col-md-3">
|
600
600
|
<div class="container device-container">
|
601
601
|
<div class="row device-os-icon">
|
602
|
-
|
602
|
+
<% if device[:type] == K::ANDROID_DEVICE %>
|
603
|
+
<i class="fa fa-android fa-lg"></i>
|
604
|
+
<% else %>
|
605
|
+
<i class="fa fa-globe fa-lg"></i>
|
606
|
+
<% end %>
|
603
607
|
</div>
|
604
|
-
<div class="row device-title"><%= device[
|
605
|
-
<div class="row device-info">ID - <%= device[
|
606
|
-
|
607
|
-
|
608
|
+
<div class="row device-title"><%= device[:model] %></div>
|
609
|
+
<div class="row device-info">ID - <%= device[:id] %></div>
|
610
|
+
<% if device[:type] == K::ANDROID_DEVICE %>
|
611
|
+
<div class="row device-info">SDK Version - <%= device[:sdk] %></div>
|
612
|
+
<div class="row device-info">Screen Size - <%= "#{device[:screen_height]}x#{device[:screen_width]}" %></div>
|
613
|
+
<% end %>
|
608
614
|
<div class="row device-info result-btn">
|
609
|
-
<a href='<%= "./#{device[
|
615
|
+
<a href='<%= "./#{device[:id]}/feature_report.html" %>'>See Results</a>
|
610
616
|
</div>
|
611
617
|
</div>
|
612
618
|
</div>
|
@@ -643,7 +649,8 @@
|
|
643
649
|
}
|
644
650
|
devices = Array.from(devicesSet);
|
645
651
|
// GRAPH
|
646
|
-
|
652
|
+
let devicesLength = devices.length > 1 ? (devices.length-1) : 1;
|
653
|
+
var height = 150*devicesLength;
|
647
654
|
var c20c = d3.scale.category10();
|
648
655
|
d3.select("#chart_row" + (i+1))
|
649
656
|
.text(jsonObject["name"])
|
@@ -125,9 +125,42 @@
|
|
125
125
|
</div>
|
126
126
|
</div>
|
127
127
|
</div>
|
128
|
-
</div>
|
128
|
+
</div> <!-- Closes scenario row -->
|
129
|
+
|
130
|
+
<div class="row">
|
131
|
+
<div class="col-md-12 col-sm-12 col-xs-12">
|
132
|
+
<div class="x_panel" style="height: auto;">
|
133
|
+
<div class="x_title">
|
134
|
+
<h2>Screenshots</h2>
|
135
|
+
<div class="clearfix"></div>
|
136
|
+
</div>
|
137
|
+
<div class="x_content">
|
138
|
+
<div class="container">
|
139
|
+
<div class="row">
|
140
|
+
<% f["steps"].each do |s| %>
|
141
|
+
<div class="col-md-2">
|
142
|
+
<div class="step_title">
|
143
|
+
<i class="fa fa-arrow-right step_title_icon"></i>
|
144
|
+
<%= s["keyword"] %> <%= s["name"] %>
|
145
|
+
</div>
|
146
|
+
<% if(s["after"] && s["after"].count > 0 && s["after"].first["embeddings"] && s["after"].first["embeddings"].count > 0) %>
|
147
|
+
<% step_image = s["after"].first["embeddings"].first["data"] %>
|
148
|
+
<img src='<%= "data:image/png;base64,#{step_image}" %>' alt="" class="img-responsive step_image" onclick='<%= "openImageNewTab(\"#{step_image}\")" %>'>
|
149
|
+
<% else %>
|
150
|
+
<div class="not_available">
|
151
|
+
NO IMAGE AVAILABLE
|
152
|
+
</div>
|
153
|
+
<% end %>
|
154
|
+
</div>
|
155
|
+
<% end %>
|
156
|
+
</div>
|
157
|
+
</div>
|
158
|
+
</div>
|
159
|
+
</div>
|
160
|
+
</div>
|
161
|
+
</div> <!-- Closes screenshots row -->
|
162
|
+
<% end %>
|
129
163
|
</div>
|
130
|
-
<% end %>
|
131
164
|
|
132
165
|
<script src="../../assets/js/jquery-3.2.1.min.js" ></script>
|
133
166
|
<script src="../../assets/js/bootstrap.min.js"></script>
|
@@ -136,39 +169,48 @@
|
|
136
169
|
<script src="../../assets/js/dataTables.responsive.min.js"></script>
|
137
170
|
<script src="../../assets/js/Chart.min.js"></script>
|
138
171
|
<script>
|
139
|
-
|
140
|
-
|
141
|
-
|
172
|
+
function openImageNewTab(imageData) {
|
173
|
+
var im = new Image();
|
174
|
+
im.src = "data:image/png;base64," + imageData
|
175
|
+
var w = window.open("",'_blank');
|
176
|
+
w.document.write(im.outerHTML);
|
177
|
+
w.document.close();
|
178
|
+
}
|
142
179
|
|
143
|
-
|
144
|
-
var scenarioOptions = {
|
145
|
-
legend: false,
|
146
|
-
responsive: false
|
147
|
-
};
|
180
|
+
$(document).ready(function () {
|
148
181
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
182
|
+
passed_scenarios = parseInt("<%= passed_scenarios(@feature).count %>")
|
183
|
+
failed_scenarios = parseInt("<%= failed_scenarios(@feature).count %>")
|
184
|
+
|
185
|
+
scenarios_data = [passed_scenarios,failed_scenarios];
|
186
|
+
var scenarioOptions = {
|
187
|
+
legend: false,
|
188
|
+
responsive: false
|
189
|
+
};
|
190
|
+
|
191
|
+
new Chart(document.getElementById("scenario-chart"), {
|
192
|
+
type: 'doughnut',
|
193
|
+
tooltipFillColor: "rgba(51, 51, 51, 0.55)",
|
194
|
+
data: {
|
195
|
+
labels: [
|
196
|
+
"Passed",
|
197
|
+
"Failed"
|
198
|
+
],
|
199
|
+
datasets: [{
|
200
|
+
data: scenarios_data,
|
201
|
+
backgroundColor: [
|
202
|
+
"#26B99A",
|
203
|
+
"#E74C3C"
|
204
|
+
]
|
205
|
+
}]
|
206
|
+
},
|
207
|
+
options: scenarioOptions
|
208
|
+
});
|
167
209
|
|
168
|
-
|
169
|
-
|
210
|
+
$('body').tooltip({
|
211
|
+
selector: '[data-toggle="tooltip"]'
|
212
|
+
});
|
170
213
|
});
|
171
|
-
});
|
172
214
|
</script>
|
173
215
|
</body>
|
174
216
|
</html>
|